"""
oyWingFeatherSetup by Erkan Ozgur Yilmaz (c) 2009

v10.7.21

Description:
------------
This is a tool for quick setup for bird wing feathers

Version History:
----------------
v10.7.21
- added autoAlignTwist deformer and controllers, which aligns the feathers to
  the correct orientation according to the current wing position

v10.7.20
- moved methods from the other Utility class to the one here

v10.4.28
- removed the import oySetProject statement
- added parametric scale control for the tip curve creation
- added methods to adjust the rootSurfaceThickness
- fixed lower and upper profile curve generation methods
- updated doSetup method

v9.12.12
- added upper and lower profile curves and upper and lower profile surfaces
  which are derived from the profile curves by using a loft. This profile
  surfaces will help to intersect or pull the body feathers on to the wing
  feathers, which are mainly done in Houdini in our pipeline for now.

v9.11.30
- added showFeathers attribute to gradually control the visibilities of the
  feathers
- the range of the setDriven keyframes in the blenderObject is scaled 10 times
  for the deformer attributes, to let the user control the object more
  precisely

v9.11.26
- sets names are fixed to clearly see the groups
- now the script accepts only one object as the featherBaseObject, so one can
  use a simple plane for every feather
- the history is now deleted when a surface is created from a curve
- now only the tip scaleZ attribute of the tip curve is freezed
- the wing attributes are now checked for existancy before they are created in
  the blend controller

v9.11.21
- fixed an syntax error while creating the blend controller attributes

v9.11.18
- moved deformerVisibility attribute over switchOpenClose attribute

v9.11.16
- added deformer visibility attribute to the blend controller and connect it
  to the deformer handles
- created bend1 bend2 and twist attribute in the blend controller for every
  guide feather in a wingPart
- added axialCorrectionGroups for the openTipJoints and closedTipJoints
- added scale constraints between openTipJoints, closedTipJoints, and tipJoints
- created new sets for up and aim locators
- both the start and end angle attributes of the twist deformer are now
  connected to the blend controller object, and the limits changed to -180,180
- the lowBound and highbound attributes of the bend1 are changed to -10,10
- the upLocators now parented to the follicles back again

v9.11.15
- the need for an extra variable for the final joint in the wing and the tip
  is eliminated by using the last joint as another wing joint with 0 feathers
- while creating the tip joints the joints are now placed on the u=0.5 line
  with the corresponding v values from the wing follicle, to get a more even
  distribution
- created setDrivenKeyframes to animate the wing from open form to closed form
- created groups to group the created nodes
- created sets to group the created nodes

v9.11.14
- the Utility method now has new methods for animation curve editing
- there are now two other copy of the original wing which are going to be used
  in animation, as the open and closed wing controls

v9.11.13
- now the final tip joint of the wing is automatically added to calculation,
  there is no need to add it as another wing joint and try to add only one
  feather on it
- now the deformer attributes of the first and last feather in a wingPart
  object are blended over the internal feathers
- the feathers are now automatically oriented according to the direction of the
  wingPart, so the same feather object can be used to decorate both the right
  and left wings

v9.11.10
- still a development version
- created WingPart, Feather, Utility classes to organize the data more freely

v9.11.9
- still the development version
- placing the base feathers are now working in its most basic form

v9.11.8
- still the development version

v9.11.7
- the development version

-------------------------------------------------------------------------------

TODO:
-----

- Assign a shader for every feather layer
- Create visibility switches for every thing
- The newly created profile surface opens up another nice possibility to align
  the feathers to each other by using their twist attribute with the profile
  surface, think about it later

Done :
------
+ Create the neccesary connections to a set driven key system
+ Create joints by duplicating the original wing joint hierarchy, to easily
  blend between closed and open poses of the wing animations
+ Create a set for wing elements
+ Create a set for every wing part
+ Create a set for feather layers
+ The nodes that needs to be connected to a set driven key system are:
  * upLocators
  * aimLocators
  * the weights of the parent constrains of the
+ Create axialCorrection groups for up vectors

"""



import pymel.core as pm
import oyAuxiliaryFunctions as oyAux


__version__ = "10.7.20"



########################################################################
class Wing(object):
    """a class to help rigging of bird wings
    """
    
    
    
    #----------------------------------------------------------------------
    def __init__(self):
        
        self._feathers = []
        self._lowerCovertFeathers = []
        self._upperCovertFeathers = []
        self._lowerGreaterCovertFeathers = []
        self._upperGreaterCovertFeathers = []
        
        self._featherCounts = 0
        self._featherBaseObject = None
        #self._featherBaseObjects = []
        
        self._rootFollicles = []
        self._tipFollicles = []
        
        self._wingParts = []
        
        self._wingJoints = []
        self._closedWingJoints = []
        self._openWingJoints = []
        
        # the parent constraints between the open, close and wing joints
        self._wingParentConstraints = []
        
        self._blender = None
        
        self._wingJointsCount = 0
        
        self._wingJointsLenghts = []
        
        self._direction = 1
        self._directionScale = 10.0
        
        self._rootSurfaceThickness = 1.0
        
        self._rootCurve = None
        self._rootProfileCurve1 = None
        self._rootProfileCurve2 = None
        self._rootSurface = None
        self._rootSurfaceSkinCluster = None
        
        self._tipCurve = None
        self._tipJoints = []
        self._closedTipJoints = []
        self._openTipJoints = []
        self._tipParentConstraints = []
        self._tipScaleConstraints = []
        self._tipSurface = None
        self._tipProfileCurve1 = None
        self._tipProfileCurve2 = None
        self._tipSurfaceSkinCluster = None
        
        self._upperProfileCurves = []
        self._lowerProfileCurves = []
        
        self._upperProfileSurface = None
        self._lowerProfileSurface = None
        
        self._wingSurface = None
        
        self._dropOff = 1
        
        self._blenderAttributeName = 'switchOpenClose'
        
        self._wingName = ''
        self._masterGroup = None
        self._rootFolliclesGroup = None
        self._tipFolliclesGroup = None
        
        self._wingJointsSet = None
        self._openWingJointsSet = None
        self._closedWingJointsSet = None
        self._tipJointsSet = None
        self._openTipJointsSet = None
        self._closedTipJointsSet= None
        self._rootCurvesAndSurfacesSet = None
        self._tipCurvesAndSurfacesSet = None
        self._rootFolliclesSet = None
        self._tipFolliclesSet = None
        self._feathersSet = None
        self._upperCovertFeathersSet = None
        self._lowerCovertFeathersSet = None
        self._upperGreaterCovertFeathersSet = None
        self._lowerGreaterCovertFeathersSet = None
        self._bend1sSet = None
        self._bend2sSet = None
        self._autoAlignTwistSet = None
        self._twistsSet = None
        self._guideBend1sSet = None
        self._guideBend2sSet = None
        self._guideTwistsSet = None
        self._upLocatorsSet = None
        self._aimLocatorsSet = None
        self._lowerProfileCurvesSet = None
        self._upperProfileCurvesSet = None
        self._upperProfileSurfaceSet = None
        self._lowerProfileSurfaceSet = None
    
    
    
    #----------------------------------------------------------------------
    def doSetup(self):
        """sets up all the feather rig
        """
        
        # SOME OLD DEVELOPMENT NOTES ( WILL BE DELETED LATER )
        # duplicate the 
        
        # every first and last feather should be a guide feather for the others
        # in the queue
        
        # add shape deformers like the x-bend and y-bend and blend them between
        # the internal feathers
        
        # create the upper secondary and lower secondary feathers
        
        # X-axis by default should be the axis that the child joint lies on
        # so, duplicate the feathers over that direction
        
        # the feather object should overlap with the z-axis on the world with
        # freezed transformations
        
        # feathers should be aligned by using aim constraints to locators,
        # which are placed over a curve, there should also be another group of 
        # locators that shouws the up axis
        
        # the guide curves for the wing tips and the up axis, should be created
        # automatically, and it should allow the user to modify them any time
        # -------------------------------------------------------------
        
        self.createRootCurve()
        self.createRootSurface()
        self.createTipCurve()
        self.createTipSurface()
        self.createTipJoints()
        self.createOpenAndClosedWingJoints()
        self.createOpenAndClosedTipJoints()
        self.createJointBlenders()
        self.createFeatherRoots()
        self.createFeatherTips()
        self.connectTipJointsToRootJoints()
        self.rigTipSurface()
        self.rigRootSurface()
        self.createFeathers()
        self.createFeatherBlenders()
        self.createBlenderAttributes(self._blender)
        self.connectBlenderController()
        self.createProfileCurvesAndSurfaces()
        self.createMasterGroups()
    
    
    
    #----------------------------------------------------------------------
    def _getDirectionScale(self):
        """gets the directionScale
        """
        return self._directionScale
    
    
    
    #----------------------------------------------------------------------
    def _setDirectionScale(self, directionScale):
        """sets the directionScale
        """
        self._directionScale = directionScale
    
    directionScale = property( _getDirectionScale, _setDirectionScale )
    
    
    
    #----------------------------------------------------------------------
    def _getWingName(self):
        """gets the wing name
        """
        return self._wingName
    
    
    
    #----------------------------------------------------------------------
    def _setWingName(self, wingName):
        """sets the wingName
        """
        self._wingName = wingName
    
    wingName = property( _getWingName, _setWingName )
    
    
    
    #----------------------------------------------------------------------
    def setWingJoints(self, joints):
        """sets the wing joints
        """
        
        self._wingJoints = joints
        # create the partial wing data object and append the joints
        self._wingParts = []
        for joint in joints:
            tempWingP = WingPart()
            tempWingP.setRootJoint( joint )
            tempWingP.featherLength = abs(self._directionScale)
            
            self._wingParts.append( tempWingP )
        
        self._wingJointsCount = len(self._wingJoints)
        # use the first joints direction
        self._direction = self._wingParts[0].getDirection()
    
    
    
    #----------------------------------------------------------------------
    def setFeatherCounts(self, featherCounts):
        """sets the feather counts for the joints
        """
        self._featherCounts = featherCounts
        
        # update the wingPart objects
        for i,count in enumerate(featherCounts):
            self._wingParts[i].setFeatherCount( count )
    
    
    
    ##----------------------------------------------------------------------
    #def setFeatherBaseObjects(self, featherBaseObjects):
        #"""sets the feather base object that is going to be copied over the
        #feather joints
        #"""
        
        #tempList = []
        
        #for feather in featherBaseObjects:
            #tempList.append( pm.PyNode( feather ) )
        
        #featherBaseObjects = tempList
        
        #self._featherBaseObjects = featherBaseObjects
        
        #for i,wingP in enumerate(self._wingParts):
            
            #try:
                #currentFeatherBaseObject = featherBaseObjects[i]
            #except IndexError:
                #wingP.setFeatherBaseObject( None )
            #else:
                #wingP.setFeatherBaseObject( currentFeatherBaseObject )
    
    
    
    #----------------------------------------------------------------------
    def setFeatherBaseObject(self, featherBaseObject):
        """sets the feather base object that is going to be copied over the
        feather joints
        """
        
        self._featherBaseObject = featherBaseObject
        
        for i,wingP in enumerate(self._wingParts):
            
            currentFeatherBaseObject = featherBaseObject
            wingP.setFeatherBaseObject( currentFeatherBaseObject )
    
    
    
    #----------------------------------------------------------------------
    def createFeatherRoots(self):
        """creates the feather root follicles
        """
        
        for wingP in self._wingParts:
            #assert(isinstance(wingP, WingPart))
            wingP.createRootFollicles()
            
            for follicle in wingP.getRootFollicles():
                self._rootFollicles.append( follicle )
    
    
    
    #----------------------------------------------------------------------
    def createFeatherBlenders(self):
        """invokes wingPart objects to create blenders between the first and
        the last feather of the wingPart
        """
        for wingP in self._wingParts:
            #assert(isinstance(wingP, WingPart))
            wingP.createFeatherBlenders()
    
    
    
    #----------------------------------------------------------------------
    def createFeatherTips(self):
        """creats the feather tip locators
        """
        
        for wingP in self._wingParts:
            #assert(isinstance(wingP, WingPart))
            wingP.createTipFollicles()
            
            for follicle in wingP.getTipFollicles():
                self._tipFollicles.append( follicle )
    
    
    
    #----------------------------------------------------------------------
    def createFeathers(self):
        """invokes wingPart objects createFeathers to create feather objects
        """
        
        self._feathers = []
        self._upperCovertFeathers = []
        self._lowerCovertFeathers = []
        self._upperGreaterCovertFeathers = []
        self._lowerGreaterCovertFeathers = []
        
        for wingP in self._wingParts:
            #assert(isinstance(wingP,WingPart))
            wingP.createFeathers()
            
            # store the feathers
            for feather in wingP.getFeathers():
                self._feathers.append( feather )
            
            for lowerCovertFeather in wingP.lowerCovertFeathers:
                self._lowerCovertFeathers.append( lowerCovertFeather )
            
            for upperCovertFeather in wingP.upperCovertFeathers:
                self._upperCovertFeathers.append( upperCovertFeather )
            
            for lowerGreaterCovertFeather in wingP.lowerGreaterCovertFeathers:
                self._lowerGreaterCovertFeathers.append( lowerGreaterCovertFeather )
            
            for upperGreaterCovertFeather in wingP.upperGreaterCovertFeathers:
                self._upperGreaterCovertFeathers.append( upperGreaterCovertFeather )
    
    
    
    #----------------------------------------------------------------------
    def createProfileCurvesAndSurfaces(self):
        """invokes the wingPart objects createProfileCurves to create the
        profile curves
        """
        
        self._upperProfileCurves = []
        self._lowerProfileCurves = []
        
        for wingP in self._wingParts:
            assert(isinstance(wingP,WingPart))
            wingP.createProfileCurves()
            
            for upperProfileCurve in wingP.upperProfileCurves:
                self._upperProfileCurves.append( upperProfileCurve )
            
            for lowerProfileCurve in wingP.lowerProfileCurves:
                self._lowerProfileCurves.append( lowerProfileCurve )
        
        #print self._upperProfileCurves
        
        # create the profile surfaces
        self._upperProfileSurface = pm.loft( self._upperProfileCurves, ar=False, n='upperProfileSurface#' )
        self._lowerProfileSurface = pm.loft( self._lowerProfileCurves, ar=False, n='lowerProfileSurface#' )
    
    
    
    #----------------------------------------------------------------------
    def createRootCurve(self):
        """creates the root curve that the featherSurface will based on
        """
        
        # use the feather counts to calculate the CV positions
        
        # to make it easy use locators to get the world positions and delete
        # the locators when ever the job is done
        
        # create a root locator
        locator = pm.spaceLocator()
        
        cvList = []
        
        # add the first joints position by hand
        cvList.append( pm.xform( self._wingJoints[0], q=True, ws=True, t=True ) )
        
        # duplicate the locator over the joints
        for i in range( self._wingJointsCount):
            
            #joint = self._wingJoints[i]
            joint = self._wingParts[i].getRootJoint()
            #featherCount = self._wingParts[i].getFeatherCount()
            divisionCount = 3
            
            newLocators = []
            newLocators = Utility.duplicateOverJoint( joint, locator, divisionCount )
            
            for aLocator in newLocators:
                cvList.append( pm.xform(aLocator,q=True,ws=True,t=True) )
            
            pm.delete( newLocators )
        
        # delete the temp locator
        pm.delete( locator )
        
        # create the curve
        self._rootCurve = Utility.createCurve( cvList )
        self._rootCurve.rename( self.wingName + '_rootCurve#' )
        
        # create a temp locator point constraint it to the first joint
        # orientConstraint it with all the wing joints
        # align the curve axis with that locator
        tempLocator = pm.spaceLocator()
        pm.pointConstraint( self._wingJoints[0], tempLocator )
        for joint in self._wingJoints:
            pm.orientConstraint( joint, tempLocator, w=1.0 )
        
        # parent it to that locator and freeze it
        pm.parent( self._rootCurve, tempLocator)
        
        pm.makeIdentity( self._rootCurve, apply=True, t=1, r=1, s=1 )
        pm.xform(self._rootCurve, os=True, piv=(0.0,0.0,0.0))
        pm.parent( self._rootCurve, w=True, a=True )
        
        # delete the tempLocator
        pm.delete( tempLocator )
        
        # set the wingParts root curves
        for wingP in self._wingParts:
            wingP.setRootCurve( self._rootCurve )
    
    
    
    #----------------------------------------------------------------------
    def createTipCurve(self):
        """creates the tip curve
        """
        
        # duplicate the rootCurve and move it along z axis
        temp = self._rootCurve.duplicate(rc=True)
        self._tipCurve = temp[0]
        
        self._tipCurve.rename( self.wingName + "_tipCurve#" )
        
        # move it a little bit
        #pm.move(self._tipCurve, (0,0,self._direction * 10.0), r=True, os=True )
        pm.move(self._tipCurve, (0,0,self._direction * self._directionScale), r=True, os=True )
        
        # set all the cvs y and z position to the first ones
        #self._tipCurve.setAttr('sy',0)
        self._tipCurve.setAttr('sz',0)
        
        pm.makeIdentity( self._tipCurve, apply=True, s=1)
        
        # set the wingParts tip curve
        for wingP in self._wingParts:
            wingP.setTipCurve( self._tipCurve )
    
    
    
    #----------------------------------------------------------------------
    def createTipJoints(self):
        """creates the joints that deform the tip surface
        """
        
        tipCurve = pm.PyNode ( self._tipCurve.getShape() )
        
        tipJointPositions = []
        
        # use pointOnSurfaceInfo node and get the world position of the uv values of the corresponding root joint
        pOSI = pm.createNode( 'pointOnSurfaceInfo' )
        # connect the tip surface
        self._tipSurface.attr('worldSpace[0]') >> pOSI.attr('inputSurface')
        
        for i in range(self._wingJointsCount):
            # get the uv value of the wingJoint
            outTra, outU, outV = Utility.closestPointOnSurface( self._wingJoints[i], self._rootSurface )
            
            # use the uv position to get the position of the tip joint
            pOSI.setAttr('u', 0.5 ) #outU)
            pOSI.setAttr('v', outV)
            
            # get the position
            tipJointPositions.append( pOSI.getAttr('result.position') )
        
        # delete the pOSI node
        pm.delete( pOSI )
        
        self._tipJoints = []
        
        for i in range( 0, self._wingJointsCount ):
            tempJoint = pm.joint( p=tipJointPositions[i] )
            pm.parent( tempJoint, self._tipCurve)
            
            pm.makeIdentity( tempJoint, apply=True, jointOrient=True )
            
            pm.parent( tempJoint, w=True )
            
            self._wingParts[i].setTipJoint( tempJoint )
            self._tipJoints.append( tempJoint )
            
            self._tipJoints[-1].rename( self.wingName + "_tipJoint#" )
            
            pm.select( None )
    
    
    
    #----------------------------------------------------------------------
    def rigTipSurface(self):
        """rigs the tip surface to tip joints of the wing parts
        """
        
        #tipJoints = []
        #for wingPart in self._wingParts:
            #tipJoints.append( wingPart.getTipJoint() )
            
        pm.select( self._tipSurface, self._tipJoints )
        
        self._tipSurfaceSkinCluster = pm.skinCluster( tsb=True, dr=self._dropOff, toSelectedBones=True )

        pm.select( None )
        
        self._tipSurfaceSkinCluster.rename( self.wingName + "_tipSurface_skinCluster#" )
        
        pm.select( None )
    
    
    
    #----------------------------------------------------------------------
    def _getRootSurfaceThickness(self):
        """returns the rootSurfaceThickness
        """
        return self._rootSurfaceThickness
    
    
    
    #----------------------------------------------------------------------
    def _setRootSurfaceThickness(self, thickness):
        """sets the rootSurfaceThickness
        """
        self._rootSurfaceThickness = thickness
    
    rootSurfaceThickness = property( _getRootSurfaceThickness, _setRootSurfaceThickness )
    
    
    
    #----------------------------------------------------------------------
    def createTipSurface(self):
        """creates the tip surface
        """
        
        self._tipSurface, self._tipProfileCurve1, self._tipProfileCurve2 = \
            Utility.createSurfaceFromCurve(
                self._tipCurve,
                (0,self._direction,0),
                self._rootSurfaceThickness
            )
        
        self._tipSurface.rename( self.wingName + "_tipSurface#" )
        self._tipProfileCurve1.rename( self.wingName + "_tipProfileCurveA#" )
        self._tipProfileCurve2.rename( self.wingName + "_tipProfileCurveB#" )
        
        # set the wingParts tip surface
        for wingP in self._wingParts:
            #assert(isinstance(wingP, WingPart ))
            wingP.setTipSurface( self._tipSurface )
        
        pm.select(None)
    
    
    
    #----------------------------------------------------------------------
    def createRootSurface(self):
        """creates the root surface
        """
        
        self._rootSurface, self._rootProfileCurve1, self._rootProfileCurve2 = \
            Utility.createSurfaceFromCurve(
                self._rootCurve,
                (0,self._direction,0),
                self._rootSurfaceThickness
            )
        
        self._rootSurface.rename( self.wingName + "_rootSurface#" )
        self._rootProfileCurve1.rename( self.wingName + "_rootProfileCurveA#" )
        self._rootProfileCurve2.rename( self.wingName + "_rootProfileCurveB#" )
        
        # set the wingParts root surface
        for wingP in self._wingParts:
            #assert(isinstance(wingP, WingPart ))
            wingP.setRootSurface( self._rootSurface )
    
    
    
    #----------------------------------------------------------------------
    def rigRootSurface(self):
        """rigs the root surface to the wing joints
        """
        
        rootJoints = []
        
        pm.select( self._rootSurface, self._wingJoints )
        self._rootSurfaceSkinCluster = pm.skinCluster( tsb=True, dr=self._dropOff )
        
        pm.select( None )
        
        self._rootSurfaceSkinCluster.rename( self.wingName + "_rootSurface_skinCluster#" )
    
    
    
    #----------------------------------------------------------------------
    def connectTipJointsToRootJoints(self):
        """invokes the WingPart classes connectTipJointsToRootJoints method
        """
        
        for wingP in self._wingParts:
            wingP.connectTipJointsToRootJoints()
    
    
    
    #----------------------------------------------------------------------
    def createOpenAndClosedWingJoints(self):
        """creates the joint chains to blend the open and closed wing
        animations
        """
        
        baseRadius = self._wingJoints[0].getAttr('radius')
        
        closedWingStartJoint = Utility.duplicateJointChain( self._wingJoints[0] )
        self._closedWingJoints = Utility.getJointList( closedWingStartJoint, self._wingJointsCount )
        
        # set their radius to two times of the previous one
        for joint in self._closedWingJoints:
            joint.setAttr( 'radius', baseRadius * 2 )
            joint.rename ( self.wingName + "_closedWingJoint#" )
        
        openWingStartJoint = Utility.duplicateJointChain( self._wingJoints[0] )
        self._openWingJoints = Utility.getJointList( openWingStartJoint, self._wingJointsCount )
        
        # set their radius to three times of the previous one
        for joint in self._openWingJoints:
            joint.setAttr( 'radius', baseRadius * 3 )
            joint.rename ( self.wingName + "_openWingJoint#" )
        
        for i, wingP in enumerate(self._wingParts):
            #assert(isinstance(wingP, WingPart))
            wingP.closedWingJoint = self._closedWingJoints[i]
            wingP.openWingJoint = self._openWingJoints[i]
        
    
    
    
    #----------------------------------------------------------------------
    def createOpenAndClosedTipJoints(self):
        """creates the joint chains to blend the open and closed tip
        animasitons
        """
        
        baseRadius = self._tipJoints[0].getAttr('radius')
        self._openTipJoints = []
        self._closedTipJoints = []
        
        for joint in self._tipJoints:
            #assert(isinstance(joint, pm.nodetypes.Joint))
            self._openTipJoints.append ( joint.duplicate(rc=True)[0] )
            self._closedTipJoints.append( joint.duplicate(rc=True)[0] )
            
            self._closedTipJoints[-1].setAttr('radius', baseRadius * 2 )
            self._openTipJoints[-1].setAttr('radius', baseRadius * 3 )
            
            self._openTipJoints[-1].rename( self.wingName + "_openTipJoint#" )
            self._closedTipJoints[-1].rename( self.wingName + "_closedTipJoint#" )
        
        # inform the wingPart objects with those new objects
        for i, wingP in enumerate(self._wingParts):
            #assert(isinstance( wingP, WingPart ))
            wingP.closedTipJoint = self._closedTipJoints[i]
            wingP.openTipJoint = self._openTipJoints[i]
    
    
    
    #----------------------------------------------------------------------
    def createJointBlenders(self):
        """creates parent constraints between open, close and rigged joints
        """
        
        # create the parent constraints
        for i in range(self._wingJointsCount):
            # wing joints
            pCnst = pm.parentConstraint( self._openWingJoints[i], self._wingJoints[i], w=1.0 )
            pCnst = pm.parentConstraint( self._closedWingJoints[i], self._wingJoints[i], w=0.0 )
            pCnst.rename( self.wingName + "_wing_parentConstraint#" )
            
            # set interpolation to shortest
            pCnst.setAttr('interpType', 2)
            
            self._wingParentConstraints.append( pCnst )
            
            # tip joints
            pCnst = pm.parentConstraint( self._openTipJoints[i], self._tipJoints[i], w=1.0 )
            pCnst = pm.parentConstraint( self._closedTipJoints[i], self._tipJoints[i], w=0.0 )
            
            sCnst = pm.scaleConstraint( self._openTipJoints[i], self._tipJoints[i], w=1.0 )
            sCnst = pm.scaleConstraint( self._closedTipJoints[i], self._tipJoints[i], w=0.0 )
            
            pCnst.rename( self.wingName + "_tip_parentConstraint#" )
            sCnst.rename( self.wingName + "_tip_scaleConstraint#" )
            
            # set interpolation to shortest
            pCnst.setAttr('interpType', 2)
            
            self._tipParentConstraints.append( pCnst )
            self._tipScaleConstraints.append( sCnst )
    
    
    
    #----------------------------------------------------------------------
    def setBlendController(self, blenderObject):
        """sets the blender object, that blends open and closed wing
        """
        
        # prefix the blender name
        #Utility.prefixNodeName( blenderObject, self.wingName )
        blenderObject.rename ( self.wingName + "_blender#" )
        
        self._blender = blenderObject
        
        ## createBlenderAttributes
        #self.createBlenderAttributes( self._blender )
    
    
    
    #----------------------------------------------------------------------
    def connectBlenderController(self):
        """connects the blender controller to the objects by using set driven
        keys
        """
        
        # the range is 0-10
        
        blenderAttrAsStr = self._blender.name() + "." + self._blenderAttributeName
        blenderAttr = pm.PyNode(blenderAttrAsStr)
        
        # wings parent constraint
        for i in range(len(self._wingParentConstraints)):
            
            # get the weight alias list
            wal = pm.parentConstraint( self._wingParentConstraints[i], q=True, wal=True)
            
            # open attr
            attr = wal[0]
            #pm.setDrivenKeyframe( self._wingParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=1.0 )
            Utility.setDrivenKeyframe( wal[0], blenderAttr, dv=0.0, v=1.0 )
            #pm.setDrivenKeyframe( self._wingParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=10.0, v=0.0 )
            Utility.setDrivenKeyframe( wal[0], blenderAttr, dv=10.0, v=0.0 )
            
            # close attr
            attr = wal[1]
            #pm.setDrivenKeyframe( self._wingParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=0.0 )
            Utility.setDrivenKeyframe( wal[1], blenderAttr, dv=0.0, v=0.0 )
            #pm.setDrivenKeyframe( self._wingParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=10.0, v=1.0 )
            Utility.setDrivenKeyframe( wal[1], blenderAttr, dv=10.0, v=1.0 )
        
        
        
        # tips parent and scale constraints
        for i in range(len(self._tipParentConstraints)):
            
            # parent constraints
            # get the weight alias list
            wal = pm.parentConstraint( self._tipParentConstraints[i], q=True, wal=True )
            
            # open attr
            attr = wal[0]
            #pm.setDrivenKeyframe( self._tipParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=1.0 )
            Utility.setDrivenKeyframe( wal[0], blenderAttr, dv=0.0, v=1.0 )
            #pm.setDrivenKeyframe( self._tipParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=10.0, v=0.0 )
            Utility.setDrivenKeyframe( wal[0], blenderAttr, dv=10.0, v=0.0 )
            
            # close attr
            attr = wal[1]
            #pm.setDrivenKeyframe( self._tipParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=0.0 )
            Utility.setDrivenKeyframe( wal[1], blenderAttr, dv=0.0, v=0.0 )
            #pm.setDrivenKeyframe( self._tipParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=10.0, v=1.0 )
            Utility.setDrivenKeyframe( wal[1], blenderAttr, dv=10.0, v=1.0 )
            
            
            # scale constraint
            wal = pm.scaleConstraint( self._tipScaleConstraints[i], q=True, wal=True )
            
            #open attr
            attr = wal[0]
            #pm.setDrivenKeyframe( self._tipScaleConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=1.0 )
            Utility.setDrivenKeyframe( wal[0], blenderAttr, dv=0.0, v=1.0 )
            #pm.setDrivenKeyframe( self._tipScaleConstraints[i], at=attr, cd=blenderAttrAsStr, dv=10.0, v=0.0 )
            Utility.setDrivenKeyframe( wal[0], blenderAttr, dv=10.0, v=0.0 )
            
            attr = wal[1]
            #pm.setDrivenKeyframe( self._tipScaleConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=0.0 )
            Utility.setDrivenKeyframe( wal[1], blenderAttr, dv=0.0, v=0.0 )
            #pm.setDrivenKeyframe( self._tipScaleConstraints[i], at=attr, cd=blenderAttrAsStr, dv=10.0, v=1.0 )
            Utility.setDrivenKeyframe( wal[1], blenderAttr, dv=10.0, v=1.0 )
        
        
        
        # upLocators positions and deformers
        for i,wingP in enumerate(self._wingParts):
            assert(isinstance(wingP, WingPart))
            
            currentFeathers = wingP.getFeathers()
            for feather in currentFeathers:
                #assert(isinstance(feather,Feather))
                
                # just create the open pose values the closed one will be prepared by the user
                #pm.setDrivenKeyframe( feather.upLocator, at='tx', cd=blenderAttrAsStr, dv=0.0, v=0.0)
                Utility.setDrivenKeyframe( feather.upLocator.tx, blenderAttr, dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.upLocator, at='ty', cd=blenderAttrAsStr, dv=0.0, v=0.0)
                Utility.setDrivenKeyframe( feather.upLocator.ty, blenderAttr, dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.upLocator, at='tz', cd=blenderAttrAsStr, dv=0.0, v=0.0)
                Utility.setDrivenKeyframe( feather.upLocator.tz, blenderAttr, dv=0.0, v=0.0 )
                
                #pm.setDrivenKeyframe( feather.upLocator, at='tx', cd=blenderAttrAsStr, dv=10.0, v=0.0)
                Utility.setDrivenKeyframe( feather.upLocator.tx, blenderAttr, dv=10.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.upLocator, at='ty', cd=blenderAttrAsStr, dv=10.0, v=0.0)
                Utility.setDrivenKeyframe( feather.upLocator.ty, blenderAttr, dv=10.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.upLocator, at='tz', cd=blenderAttrAsStr, dv=10.0, v=0.0)
                Utility.setDrivenKeyframe( feather.upLocator.tz, blenderAttr, dv=10.0, v=0.0 )
                
                #pm.setDrivenKeyframe( feather.aimLocator, at='tx', cd=blenderAttrAsStr, dv=0.0, v=0.0)
                Utility.setDrivenKeyframe( feather.aimLocator.tx, blenderAttr, dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.aimLocator, at='ty', cd=blenderAttrAsStr, dv=0.0, v=0.0)
                Utility.setDrivenKeyframe( feather.aimLocator.ty, blenderAttr, dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.aimLocator, at='tz', cd=blenderAttrAsStr, dv=0.0, v=0.0)
                Utility.setDrivenKeyframe( feather.aimLocator.tz, blenderAttr, dv=0.0, v=0.0 )
                
                #pm.setDrivenKeyframe( feather.aimLocator, at='tx', cd=blenderAttrAsStr, dv=10.0, v=0.0)
                Utility.setDrivenKeyframe( feather.aimLocator.tx, blenderAttr, dv=10.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.aimLocator, at='ty', cd=blenderAttrAsStr, dv=10.0, v=0.0)
                Utility.setDrivenKeyframe( feather.aimLocator.ty, blenderAttr, dv=10.0, v=0.0 )
                #pm.setDrivenKeyframe( feather.aimLocator, at='tz', cd=blenderAttrAsStr, dv=10.0, v=0.0)
                Utility.setDrivenKeyframe( feather.aimLocator.tz, blenderAttr, dv=10.0, v=0.0 )
                
                # visibilities of deformer handles
                bend1 = feather.getBend1()[1]
                bend2 = feather.getBend2()[1]
                twist = feather.getTwist()[1]
                autoAlignTwist = feather.getAutoAlignTwist()[1]
                self._blender.attr('deformerVisibility') >> bend1.attr('v')
                self._blender.attr('deformerVisibility') >> bend2.attr('v')
                self._blender.attr('deformerVisibility') >> twist.attr('v')
                self._blender.attr('deformerVisibility') >> autoAlignTwist.attr('v')
                
                
            # connect blend attributes to the first and last feather
            if len( currentFeathers ) > 0:
                # bend1
                attrName = 'w' + str(i) + '_Bend1Start'
                bend1Start = currentFeathers[0].getBend1()
                bend1StartDeformer = bend1Start[0]
                bend1StartHandle = bend1Start[1]
                #pm.setDrivenKeyframe( bend1StartDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                Utility.setDrivenKeyframe( bend1StartDeformer.curvature, self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                #pm.setDrivenKeyframe( bend1StartDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( bend1StartDeformer.curvature, self._blender.attr( attrName ), dv=0, v=0 )
                #pm.setDrivenKeyframe( bend1StartDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=10.0, v=1.0 )
                Utility.setDrivenKeyframe( bend1StartDeformer.curvature, self._blender.attr( attrName ), dv=10.0, v=1.0 )
                
                attrName = 'w' + str(i) + '_Bend1End'
                bend1End = currentFeathers[-1].getBend1()
                bend1EndDeformer = bend1End[0]
                bend1EndHandle = bend1End[1]
                #pm.setDrivenKeyframe( bend1EndDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                Utility.setDrivenKeyframe( bend1EndDeformer.curvature, self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                #pm.setDrivenKeyframe( bend1EndDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( bend1EndDeformer.curvature, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( bend1EndDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=10.0, v=1.0 )
                Utility.setDrivenKeyframe( bend1EndDeformer.curvature, self._blender.attr( attrName ), dv=10.0, v=1.0 )
                
                # bend2
                attrName = 'w' + str(i) + '_Bend2Start'
                bend2Start = currentFeathers[0].getBend2()
                bend2StartDeformer = bend2Start[0]
                bend2StartHandle = bend2Start[1]
                #pm.setDrivenKeyframe( bend2StartDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                Utility.setDrivenKeyframe( bend2StartDeformer.curvature, self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                #pm.setDrivenKeyframe( bend2StartDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( bend2StartDeformer.curvature, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( bend2StartDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=10.0, v=1.0 )
                Utility.setDrivenKeyframe( bend2StartDeformer.curvature, self._blender.attr( attrName ), dv=10.0, v=1.0 )
                
                attrName = 'w' + str(i) + '_Bend2End'
                bend2End = currentFeathers[-1].getBend2()
                bend2EndDeformer = bend2End[0]
                bend2EndHandle = bend2End[1]
                #pm.setDrivenKeyframe( bend2EndDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                Utility.setDrivenKeyframe( bend2EndDeformer.curvature, self._blender.attr( attrName ), dv=-10.0, v=-1.0 )
                #pm.setDrivenKeyframe( bend2EndDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( bend2EndDeformer.curvature, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( bend2EndDeformer, at='curvature', cd=self._blender.attr( attrName ), dv=10.0, v=1.0 )
                Utility.setDrivenKeyframe( bend2EndDeformer.curvature, self._blender.attr( attrName ), dv=10.0, v=1.0 )
                
                # twist
                attrName = 'w' + str(i) + '_TwistStartRootAngle'
                twistStart = currentFeathers[0].getTwist()
                twistStartDeformer = twistStart[0]
                twistStartHandle = twistStart[1]
                # start/root angle
                #pm.setDrivenKeyframe( twistStartDeformer, at='startAngle', cd=self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                Utility.setDrivenKeyframe( twistStartDeformer.startAngle, self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                #pm.setDrivenKeyframe( twistStartDeformer, at='startAngle', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( twistStartDeformer.startAngle, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( twistStartDeformer, at='startAngle', cd=self._blender.attr( attrName ), dv=10.0, v=180.0 )
                Utility.setDrivenKeyframe( twistStartDeformer.startAngle, self._blender.attr( attrName ), dv=10.0, v=180.0 )
                # end/tip angle
                attrName = 'w' + str(i) + '_TwistStartTipAngle'
                #pm.setDrivenKeyframe( twistStartDeformer, at='endAngle', cd=self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                Utility.setDrivenKeyframe( twistStartDeformer.endAngle, self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                #pm.setDrivenKeyframe( twistStartDeformer, at='endAngle', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( twistStartDeformer.endAngle, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( twistStartDeformer, at='endAngle', cd=self._blender.attr( attrName ), dv=10.0, v=180.0 )
                Utility.setDrivenKeyframe( twistStartDeformer.endAngle, self._blender.attr( attrName ), dv=10.0, v=180.0 )
                
                attrName = 'w' + str(i) + '_TwistEndRootAngle'
                twistEnd = currentFeathers[-1].getTwist()
                twistEndDeformer = twistEnd[0]
                twistEndHandle = twistEnd[1]
                # start/root angle
                #pm.setDrivenKeyframe( twistEndDeformer, at='startAngle', cd=self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                Utility.setDrivenKeyframe( twistEndDeformer.startAngle, self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                #pm.setDrivenKeyframe( twistEndDeformer, at='startAngle', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( twistEndDeformer.startAngle, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( twistEndDeformer, at='startAngle', cd=self._blender.attr( attrName ), dv=10.0, v=180.0 )
                Utility.setDrivenKeyframe( twistEndDeformer.startAngle, self._blender.attr( attrName ), dv=10.0, v=180.0 )
                # end/tip angle
                attrName = 'w' + str(i) + '_TwistEndTipAngle'
                #pm.setDrivenKeyframe( twistEndDeformer, at='endAngle', cd=self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                Utility.setDrivenKeyframe( twistEndDeformer.endAngle, self._blender.attr( attrName ), dv=-10.0, v=-180.0 )
                #pm.setDrivenKeyframe( twistEndDeformer, at='endAngle', cd=self._blender.attr( attrName ), dv=0.0, v=0.0 )
                Utility.setDrivenKeyframe( twistEndDeformer.endAngle, self._blender.attr( attrName ), dv=0.0, v=0.0 )
                #pm.setDrivenKeyframe( twistEndDeformer, at='endAngle', cd=self._blender.attr( attrName ), dv=10.0, v=180.0 )
                Utility.setDrivenKeyframe( twistEndDeformer.endAngle, self._blender.attr( attrName ), dv=10.0, v=180.0 )
                
                
                # -- FEATHER VISIBILITES --
                # connect the showFeathers to the feathers visibility attr
                
                # get all the base feathers
                for feather in wingP.feathers:
                    
                    #assert(isinstance(feather, Feather))
                    
                    pm.setDrivenKeyframe( feather.getFeatherObject() , at='v', cd=self._blender.attr( 'showFeathers' ), dv=0, v=0 )
                    pm.setDrivenKeyframe( feather.getFeatherObject() , at='v', cd=self._blender.attr( 'showFeathers' ), dv=1, v=1 )
                
                # the lower covert feathers
                for cFeather in wingP.lowerCovertFeathers:
                    pm.setDrivenKeyframe( cFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=1, v=0 )
                    pm.setDrivenKeyframe( cFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=2, v=1 )
                
                # the upper covert feathers
                for cFeather in wingP.upperCovertFeathers:
                    pm.setDrivenKeyframe( cFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=1, v=0 )
                    pm.setDrivenKeyframe( cFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=2, v=1 )
                
                # the lower greater covert feathers
                for gCFeather in wingP.lowerGreaterCovertFeathers:
                    pm.setDrivenKeyframe( gCFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=1, v=0 )
                    pm.setDrivenKeyframe( gCFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=2, v=1 )
                
                # the lower greater covert feathers
                for gCFeather in wingP.upperGreaterCovertFeathers:
                    pm.setDrivenKeyframe( gCFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=1, v=0 )
                    pm.setDrivenKeyframe( gCFeather.getFeatherObject(), at='v', cd=self._blender.attr( 'showFeathers' ), dv=2, v=1 )
    
    
    
    #----------------------------------------------------------------------
    def createBlenderAttributes(self, blenderObject):
        """creates the blender attributes in the blender object
        """
        
        
        #assert(isinstance(blenderObject, pm.nodetypes.Transform) )
        
        # deformer visibilities
        if not blenderObject.hasAttr('deformerVisibility'):
            blenderObject.addAttr('deformerVisibility', at='long', min=0, max=1, defaultValue=0 )
            blenderObject.setAttr('deformerVisibility', e=True, cb=True )
        
        # showFeathers
        if not blenderObject.hasAttr('showFeathers'):
            blenderObject.addAttr('showFeathers', at='long', min=0, max=2, defaultValue=0 )
            blenderObject.setAttr('showFeathers', e=True, cb=True )
        
        # switchOpenClose
        if not blenderObject.hasAttr(self._blenderAttributeName):
            blenderObject.addAttr(self._blenderAttributeName, at='float', k=True, min=0, max=10, defaultValue=0.0)
        
        # autoAlign twist
        attrName = 'autoAlign'
        if not blenderObject.hasAttr(attrName):
            blenderObject.addAttr(attrName, at='float', k=True, min=0, max=1, defaultValue=1.0)
        
        # deformers
        for i,wingP in enumerate(self._wingParts):
            
            # if there are any feathers
            if len(wingP.getFeathers()) > 0:
                # create wingPi_bend1, wingPi_bend2, wingPi_twist for every wingPart objects
                # separator
                attrName = 'w' + str(i)
                if not blenderObject.hasAttr( attrName ):
                    blenderObject.addAttr( attrName, at='enum', enumName='---:0', k=True )
                    blenderObject.attr(attrName).lock()
                
                # bend1
                attrName = 'w' + str(i) + '_Bend1Start'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                attrName = 'w' + str(i) + '_Bend1End'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                # bend2
                attrName = 'w' + str(i) + '_Bend2Start'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                attrName = 'w' + str(i) + '_Bend2End'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                # twist
                attrName = 'w' + str(i) + '_TwistStartRootAngle'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                attrName = 'w' + str(i) + '_TwistStartTipAngle'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                attrName = 'w' + str(i) + '_TwistEndRootAngle'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
                
                attrName = 'w' + str(i) + '_TwistEndTipAngle'
                if not blenderObject.hasAttr(attrName):
                    blenderObject.addAttr(attrName, at='float', k=True, defaultValue=0.0 )
    
    
    
    #----------------------------------------------------------------------
    def createMasterGroups(self):
        """creates a group that holds all the nodes except the parented tip
        joints
        """
        
        pm.select(None)
        self._masterGroup = pm.group(em=True, n=self._wingName + "_masterSetupGroup#")
        masterGroup = self._masterGroup
        
        # the curves and surfaces
        # root
        pm.parent( self._rootCurve, masterGroup )
        pm.parent( self._rootProfileCurve1, masterGroup )
        pm.parent( self._rootProfileCurve2, masterGroup )
        pm.parent( self._rootSurface, masterGroup )
        
        # tip
        pm.parent( self._tipCurve, masterGroup )
        pm.parent( self._tipProfileCurve1, masterGroup )
        pm.parent( self._tipProfileCurve2, masterGroup )
        pm.parent( self._tipSurface, masterGroup )
        
        # create rootFollicles group
        self._rootFolliclesGroup = pm.group ( em=True, n=self._wingName + "_rootFolliclesGroup#")
        pm.parent( self._rootFollicles, self._rootFolliclesGroup )
        
        # create tipFollicles group
        self._tipFolliclesGroup = pm.group ( em=True, n=self._wingName + "_tipFolliclesGroup#" )
        pm.parent( self._tipFollicles, self._tipFolliclesGroup )
        
        # parent to the main group
        pm.parent( self._rootFolliclesGroup, masterGroup )
        pm.parent( self._tipFolliclesGroup, masterGroup )
        
        # thats all about grouping
        # lets create sets
        
        # create a sets for:
        # the wingJoints
        # the open wingJoints
        # the closed wingJoints
        # the tipJoints
        # the open tipJoints
        # the closed tipJoints
        # root curves, profile curves, surfaces etc
        # tip curves, profile curves, surfaces etc.
        # root follicles
        # tip follicles
        # feathers
        # upper covert feathers
        # lower covert feathers
        # upper greater covert feathers
        # lower greater covert feathers
        # deformer handles: bend1, bend2, twist
        
        self._wingJointsSet = pm.sets( self._wingJoints, name=self._wingName + '_wing_baseJointsSet#' )
        self._openWingJointsSet = pm.sets( self._openWingJoints, name=self._wingName + '_wing_openJointsSet#' )
        self._closedWingJointsSet = pm.sets( self._closedWingJoints, name=self._wingName + '_wing_closedJointsSet#' )
        self._tipJointsSet = pm.sets( self._tipJoints, name=self._wingName + '_tip_baseJointsSet#' )
        self._openTipJointsSet = pm.sets( self._openTipJoints, name=self._wingName + '_tip_openJointsSet#' )
        self._closedTipJointsSet = pm.sets( self._closedTipJoints, name=self._wingName + '_tip_closedJointsSet#' )
        self._rootCurvesAndSurfacesSet = pm.sets( self._rootCurve, self._rootProfileCurve1, self._rootProfileCurve2, self._rootSurface, name=self._wingName + '_curvesAndSurfaces_rootSet#' )
        self._tipCurvesAndSurfacesSet = pm.sets( self._tipCurve, self._tipProfileCurve1, self._tipProfileCurve2, self._tipSurface , name=self._wingName + '_curvesAndSurfaces_tipSet#' )
        self._rootFolliclesSet = pm.sets( self._rootFollicles, name=self._wingName + '_follicles_rootSet#' )
        self._tipFolliclesSet = pm.sets( self._tipFollicles, name=self._wingName + '_follicles_tipSet#' )
        self._feathersSet = pm.sets( map( Feather.getFeatherObject, self._feathers ), name=self._wingName + '_feathers_baseSet#' )
        self._lowerCovertFeathersSet = pm.sets( map( Feather.getFeatherObject, self._lowerCovertFeathers ), name=self._wingName + '_feathers_lowerCovertSet#' )
        self._upperCovertFeathersSet = pm.sets( map( Feather.getFeatherObject, self._upperCovertFeathers ), name=self._wingName + '_feathers_upperCovertSet#' )
        self._lowerGreaterCovertFeathersSet = pm.sets( map( Feather.getFeatherObject, self._lowerGreaterCovertFeathers), name=self.wingName + '_feathers_lowerGreaterCovertSet#' )
        self._upperGreaterCovertFeathersSet = pm.sets( map( Feather.getFeatherObject, self._upperGreaterCovertFeathers), name=self.wingName + '_feathers_upperGreaterCovertSet#' )
        
        self._lowerProfileCurvesSet = pm.sets( self._lowerProfileCurves, name=self._wingName + '_curvesAndSurfaces_lowerProfileCurvesSet#' )
        self._upperProfileCurvesSet = pm.sets( self._upperProfileCurves, name=self._wingName + '_curvesAndSurfaces_upperProfileCurvesSet#' )
        
        self._lowerProfileSurfaceSet = pm.sets( self._lowerProfileSurface, name=self._wingName + '_curvesAndSurfaces_lowerProfileSurfaceSet#' )
        self._upperProfileSurfaceSet = pm.sets( self._upperProfileSurface, name=self._wingName + '_curvesAndSurfaces_upperProfileSurfaceSet#' )
        
        bend1s = []
        bend2s = []
        twists = []
        autoTwists = []
        
        bend1Guides = []
        bend2Guides = []
        twistGuides = []
        autoTwistGuides = []
        
        upLocators = []
        aimLocators = []
        
        for wingP in self._wingParts:
            assert(isinstance(wingP, WingPart))
            feathers = wingP.getFeathers()
            for feather in feathers:
                assert(isinstance(feather, Feather))
                bend1s.append( feather.getBend1()[0] )
                bend2s.append( feather.getBend2()[0] )
                twists.append( feather.getTwist()[0] )
                autoTwists.append( feather.getAutoAlignTwist()[0] )
                
                upLocators.append( feather.getUpLocator() )
                aimLocators.append( feather.getAimLocator() )
            
            # guide deformers
            if len(feathers) > 0:
                bend1Guides.append( feathers[0].getBend1()[0] )
                bend1Guides.append( feathers[-1].getBend1()[0] )
                bend2Guides.append( feathers[0].getBend2()[0] )
                bend2Guides.append( feathers[-1].getBend2()[0] )
                twistGuides.append( feathers[0].getTwist()[0] )
                twistGuides.append( feathers[-1].getTwist()[0] )
        
        self._bend1sSet = pm.sets( bend1s, name=self._wingName + '_deformers_bend1sSet' )
        self._bend2sSet = pm.sets( bend2s, name=self._wingName + '_deformers_bend2sSet' )
        self._twistsSet = pm.sets( twists, name=self._wingName + '_deformers_twistsSet' )
        self._autoAlignTwistSet = pm.sets( autoTwists, name=self._wingName + '_deformers_autoTwistsSet' )
        
        self._guideBend1sSet = pm.sets( bend1Guides, name=self._wingName + '_deformers_bend1GuidesSet' )
        self._guideBend2sSet = pm.sets( bend2Guides, name=self._wingName + '_deformers_bend2GuidesSet' )
        self._guideTwistsSet = pm.sets( twistGuides, name=self._wingName + '_deformers_twistGuidesSet' )
        
        self._upLocatorsSet = pm.sets( upLocators, name=self._wingName + '_locators_upLocatorsSet' )
        self._aimLocatorsSet = pm.sets( aimLocators, name=self._wingName + '_locators_aimLocatorsSet' )
        
        # TODO: create the character set
        






########################################################################
class WingPart(object):
    """a little helper class to hold partial wing data, like the wing joint,
    the tip joint for that wing joint and feather root and tip follicles
    """
    
    #----------------------------------------------------------------------
    def __init__(self):
        
        self._rootJoint = None
        self._tipJoint = None
        
        self._rootFollicleParameters = []
        self._tipFollicleParameters = []
        
        self._rootFollicles = []
        self._rootFollicleTransforms = []
        self._rootFollicleCount = 0
        
        self._tipFollicles = []
        self._tipFollicleCount = 0
        
        self._rootSurface = None
        self._rootCurve = None
        self._tipSurface = None
        self._tipCurve = None
        
        self._wingLength = 0
        
        self._direction = 1 # by default
        
        self._featherCount = 0
        
        self._featherBaseObject = None
        
        self._feathers = []
        self._upperCovertFeathers = [] # the second layer
        self._lowerCovertFeathers = []
        self._upperGreaterCovertFeathers = [] # the third layer
        self._lowerGreaterCovertFeathers = [] # the third layer
        
        self._openWingJoint = None
        self._closedWingJoint = None
        
        self._openTipJoint = None
        self._closedTipJoint = None
        
        self._upperProfileCurves = []
        self._lowerProfileCurves = []

        self._featherLength = 10.0
    
    #----------------------------------------------------------------------
    #def appendTipFollicle(self, follicle, follicleParent):
    def appendTipFollicle(self, follicle):
        """appends a new tip follicle to the list
        """
        
        self._tipFollicles.append(follicle)
        #self._tipFollicleParents.append(follicleParent)
        self._tipFollicleParameters.append([follicle.getAttr('pu'), follicle.getAttr('pv')])
    
    
    
    #----------------------------------------------------------------------
    def appendRootFollicle(self, follicle):
        """appends a new root follicle to the list
        """
        
        self._rootFollicles.append( follicle )
        follicleShape = follicle.getShape()
        self._rootFollicleParameters.append( [follicleShape.getAttr('pu'), follicleShape.getAttr('pv') ] )
    
    
    
    #----------------------------------------------------------------------
    def setRootJoint(self, joint):
        """sets the wing joint
        """
        self._rootJoint = joint
        
        # update the wing length
        self._wingLength = Utility.getJointLength( self._rootJoint )
        
        if self._wingLength < 0:
            self._direction = -1
        if self._wingLength == 0:
            # get the direction from the joint x position
            if joint.getAttr('tx') < 0:
                self._direction = -1
    
    
    
    #----------------------------------------------------------------------
    def getRootJoint(self):
        """returns the root joint
        """
        return self._rootJoint
    
    
    
    #----------------------------------------------------------------------
    def getRootFollicles(self):
        """returns the root follicles
        """
        return self._rootFollicles
    
    
    
    #----------------------------------------------------------------------
    def setRootFollicles(self, rootFollicles):
        """sets the root follicles
        """
        self._rootFollicles = rootFollicles
    
    
    
    #----------------------------------------------------------------------
    def getTipFollicles(self):
        """returns the tip follicles
        """
        return self._tipFollicles
    
    
    
    #----------------------------------------------------------------------
    def setTipFollicles(self, tipFollicles):
        """sets the tip follicles
        """
        self._tipFollicles = tipFollicles
    
    
    
    #----------------------------------------------------------------------
    def setRootCurve(self, curve):
        """sets the root curve
        """
        self._rootCurve = curve
    
    
    
    #----------------------------------------------------------------------
    def getRootCurve(self):
        """returns the root curve
        """
        return self._rootCurve
    
    
    
    #----------------------------------------------------------------------
    def setTipCurve(self, curve):
        """sets the tip curve
        """
        self._tipCurve = curve
    
    
    
    #----------------------------------------------------------------------
    def getTipJoint(self):
        """returns the tip joint
        """
        return self._tipJoint
    
    
    
    #----------------------------------------------------------------------
    def setTipJoint(self, joint):
        """sets the tip joint
        """
        self._tipJoint = joint
    
    
    
    #----------------------------------------------------------------------
    def setRootFollicles(self, follicles):
        """sets the root follicles
        """
        self._rootFollicles = follicles
        self._rootFollicleCount = len( follicles )
    
    
    
    #----------------------------------------------------------------------
    def setTipFollicles(self, follicles):
        """set the tip follicles
        """
        self._tipFollicles = follicles
        self._tipFollicleCount = len( follicles )
    
    
    
    #----------------------------------------------------------------------
    def setRootSurface(self, surface):
        """sets the root surface
        """
        self._rootSurface = surface
    
    
    
    #----------------------------------------------------------------------
    def setTipSurface(self, surface):
        """sets the tip surface
        """
        self._tipSurface = surface
    
    
    
    #----------------------------------------------------------------------
    def getWingLength(self):
        """returns teh wing length
        """
        return self._wingLength
    
    
    
    #----------------------------------------------------------------------
    def setWingLength(self, wingLength):
        """sets the current partial wing length
        """
        self._wingLength = wingLength
    
    
    
    #----------------------------------------------------------------------
    def setWingDirection(self, direction):
        """sets the wing direction, for wings that the child lies on the
        negative axis the direction is negative
        """
        if direction < 0:
            self._direction = -1
    
    
    
    #----------------------------------------------------------------------
    def getFeatherCount(self):
        """returns the feather count
        """
        return self._featherCount
    
    
    
    #----------------------------------------------------------------------
    def setFeatherCount(self, featherCount):
        """sets the feather count over that part of the wing
        """
        # do not allow negative numbers
        self._featherCount = max(0,featherCount)
    
    
    
    #----------------------------------------------------------------------
    def getFeathers(self):
        """returns the feathers
        """
        return self._feathers
    
    
    
    #----------------------------------------------------------------------
    def setFeathers(self, feathers):
        """sets the feathers
        """
        self._feathers = feathers
    
    feathers = property( getFeathers, setFeathers )
    
    
    
    #----------------------------------------------------------------------
    @property
    def upperProfileCurves(self):
        """returns the upper profile curves
        """
        return self._upperProfileCurves
    
    
    
    #----------------------------------------------------------------------
    @property
    def lowerProfileCurves(self):
        """returns the lower profile curves
        """
        return self._lowerProfileCurves
    
    
    
    #----------------------------------------------------------------------
    @property
    def upperCovertFeathers(self):
        """returns upper covert feathers
        """
        return self._upperCovertFeathers
    
    
    
    #----------------------------------------------------------------------
    @property
    def lowerCovertFeathers(self):
        """returns lower covert feathers
        """
        return self._lowerCovertFeathers
    
    
    
    #----------------------------------------------------------------------
    @property
    def upperGreaterCovertFeathers(self):
        """returns upper greater covert feathers
        """
        return self._upperGreaterCovertFeathers
    
    
    
    #----------------------------------------------------------------------
    @property
    def lowerGreaterCovertFeathers(self):
        """returns lower greater covert feathers
        """
        return self._lowerGreaterCovertFeathers
    
    
    
    #----------------------------------------------------------------------
    def _getOpenTipJoint(self):
        """get the open wing joint
        """
        return self._openTipJoint
    
    
    
    #----------------------------------------------------------------------
    def _setOpenTipJoint(self, joint):
        """set the open tip joint
        """
        self._openTipJoint = joint
    
    openTipJoint = property( _getOpenTipJoint, _setOpenTipJoint )
    
    
    
    #----------------------------------------------------------------------
    def _getClosedTipJoint(self):
        """get the closed tip joint
        """
        return self._closedTipJoint
    
    
    
    #----------------------------------------------------------------------
    def _setClosedTipJoint(self, joint):
        """set the closed tip joint
        """
        self._closedTipJoint = joint
    
    closedTipJoint = property( _getClosedTipJoint, _setClosedTipJoint )
    
    
    
    #----------------------------------------------------------------------
    def _getOpenWingJoint(self):
        """get the open wing joint
        """
        return self._openWingJoint
    
    
    
    #----------------------------------------------------------------------
    def _setOpenWingJoint(self, joint):
        """set the open wing joint
        """
        self._openWingJoint = joint
    
    openWingJoint = property( _getOpenWingJoint, _setOpenWingJoint )
    
    
    
    #----------------------------------------------------------------------
    def _getClosedWingJoint(self):
        """get the closed wing joint
        """
        self._closedWingJoint
    
    
    
    #----------------------------------------------------------------------
    def _setClosedWingJoint(self, joint):
        """set the closed wind joint
        """
        self._closedWingJoint = joint
    
    closedWingJoint = property( _getClosedWingJoint, _setClosedWingJoint )
    
    
    
    #----------------------------------------------------------------------
    def setFeatherBaseObject(self, featherBaseObject):
        """sets the feather object
        """
        self._featherBaseObject = featherBaseObject
    
    
    
    #----------------------------------------------------------------------
    def getDirection(self):
        """returns the direction
        """
        return self._direction
    
    
    
    #----------------------------------------------------------------------
    def createRootFollicles(self):
        """creates the root follicle
        """
        
        if self._featherCount < 1:
            return
        
        # create a locator
        rootLocator = pm.spaceLocator()
        
        # duplicate it over the wing joint
        newLocators = Utility.duplicateOverJoint( self._rootJoint, rootLocator, self._featherCount )
        
        for locator in newLocators:
            data = Utility.closestPointOnSurface( locator, self._rootSurface )
            
            self._rootFollicleParameters.append( data )
        
        self._rootFollicleCount = len(self._rootFollicleParameters)
        
        # delete the locators
        pm.delete( newLocators, rootLocator )
        
        # use the data to create hair follicles
        self._rootFollicles = []
        
        for i in range( self._rootFollicleCount ):
            newFollicle = Utility.createFollicle(
                self._rootSurface,
                self._rootFollicleParameters[i][1],
                self._rootFollicleParameters[i][2]
            )

            # TODO: Fix this patch
            #newFollicleParent = newFollicle.getParent()
            #print "newFollicleParent: %s" % newFollicleParent
            
            self._rootFollicles.append(newFollicle)
            #self._rootFollicleTransforms.append( newFollicleParent )
        
        pm.select(None)
    
    
    
    #----------------------------------------------------------------------
    def createTipFollicles(self):
        """creates the tip follicle
        """
        
        # if there is no feather on this wingPart do not create any tip follicle
        if self._featherCount < 1:
            return
        
        # create the tip follicles using the root follicle parameters on tip surface
        self._tipFollicles = []
        self._tipFollicleParameters = []
        self._tipFollicleCount = self._rootFollicleCount
        
        for i in range( self._tipFollicleCount ):
            newFollicle = Utility.createFollicle( self._tipSurface, self._rootFollicleParameters[i][1], self._rootFollicleParameters[i][2] )
            #f_parent = newFollicle.getParent()
            #self.appendTipFollicle( newFollicle, f_parent )
            self.appendTipFollicle(newFollicle)
    
    
    
    #----------------------------------------------------------------------
    def createFeathers(self):
        """creates the feathers
        """
        
        # duplicate the featherBaseObject
        # and parent them under the follicles
        
        self.featherObjects = []
        
        featherCount = self._featherCount
        
        self._feathers = []
        
        if featherCount < 1:
            #featherCount = 1
            return
        
        
        for i in range(featherCount):
            # create a new Feather object
            #feather = Feather(self, self._rootFollicleTransforms[i], self._tipFollicleParents[i], self._featherBaseObject)
            feather = Feather(
                self,
                self._rootFollicles[i],
                self._tipFollicles[i],
                self._featherBaseObject
            )
            feather.direction = self._direction
            feather.length = self.featherLength
            
            feather.create()
            
            self._feathers.append( feather )
            
            if i != 0:
                feather.prevFeather = self._feathers[i-1]
        
        for i in range(0,featherCount-1):
            self._feathers[i].nextFeather = self._feathers[i+1]
        
        # the first feather should be a guide feather
        self._feathers[0].isGuide = True
        
        # create covert feathers
        # second layer
        self._upperCovertFeathers = self.createCovertFeathers( 0.5, 0.5 )
        self._lowerCovertFeathers = self.createCovertFeathers( -0.5, 0.5 )
        
        # third layer
        self._upperGreaterCovertFeathers = self.createCovertFeathers( 0.75, 0.25 )
        self._lowerGreaterCovertFeathers = self.createCovertFeathers( -0.75, 0.25 )
    
    
    
    #----------------------------------------------------------------------
    def createProfileCurves(self):
        """creates the profile curves for upper and lower part of the wing
        """
        
        # just create curves in 10 unit length and add them to the bend2 deformer
        
        # create a curve pointing to +Z direction then parent it under
        # feathers transform parent
        
        self._upperProfileCurves = []
        self._lowerProfileCurves = []
        
        # create cvPoint positions
        curveCVPos = []
        for i in range(11):
            curveCVPos.append( (0,0,i) )
        
        for i in range(self._featherCount):
            # the feather
            feather = self._feathers[i]
            
            # get the transform parent of the feather
            transformParent = feather.getTransformParent()
            
            # the upper profile curve
            upperProfileCurve = Utility.createCurve( curveCVPos )
            upperProfileCurve.rename( 'upperProfileCurve#' )
            
            # the lower profile curve
            lowerProfileCurve = Utility.createCurve( curveCVPos )
            lowerProfileCurve.rename( 'lowerProfileCurve#' )
            
            # parent them under the transform parent
            pm.parent( upperProfileCurve, transformParent, r=True )
            pm.parent( lowerProfileCurve, transformParent, r=True )
            
            # attach them to the bend and twist deformers
            
            upperProfileCurveShape = upperProfileCurve.getShape()
            lowerProfileCurveShape = lowerProfileCurve.getShape()
            
            bend1 = feather.getBend1()[0]
            pm.nonLinear( bend1, edit=True, geometry=upperProfileCurveShape )
            pm.nonLinear( bend1, edit=True, geometry=lowerProfileCurveShape ) 
            
            twist = feather.getTwist()[0]
            pm.nonLinear( twist, edit=True, geometry=upperProfileCurveShape )
            pm.nonLinear( twist, edit=True, geometry=lowerProfileCurveShape ) 
            
            bend2 = feather.getBend2()[0]
            pm.nonLinear( bend2, edit=True, geometry=upperProfileCurveShape )
            pm.nonLinear( bend2, edit=True, geometry=lowerProfileCurveShape ) 
            
            # save the curves
            self._upperProfileCurves.append( upperProfileCurve )
            self._lowerProfileCurves.append( lowerProfileCurve )
    
    
    
    #----------------------------------------------------------------------
    def createCovertFeathers(self, offset = 0.0, scale=1.0):
        """creates the second layer of feathes in the upper part
        """
        # duplicate the featherBaseObject
        # and parent them under the follicles
        
        featherCount = self._featherCount
        
        if featherCount == 0:
            #featherCount = 1
            return
        
        featherArray = []
        
        for i in range(featherCount):
            # create a new Feather object
            covertFeather = Feather(
                self,
                self._rootFollicles[i],
                self._tipFollicles[i],
                self._featherBaseObject,
                self._feathers[i].upLocator,
                self._feathers[i].aimLocator,
                self._feathers[i].bend1,
                self._feathers[i].bend2,
                self._feathers[i].twist,
                self._feathers[i].autoAlignTwist,
                self._feathers[i].transformParent,
                self._direction
            )
            
            covertFeather.create()
            
            featherArray.append( covertFeather )
            
            if i != 0:
                covertFeather.prevFeather = featherArray[i-1]
            
            # move the feathers a little bit higher
            #print "covertFeather.featherObject: %s" % covertFeather.featherObject
            covertFeather.featherObject.setAttr('ty', offset)
            covertFeather.featherObject.setAttr('s', (scale,scale,scale))
        
        for i in range(0,featherCount-1):
            featherArray[i].nextFeather = featherArray[i+1]
        
        # the first feather should be a guide feather
        featherArray[0].isGuid = True
        
        return featherArray
    
    
    
    #----------------------------------------------------------------------
    def connectTipJointsToRootJoints(self):
        """connects the tip joint to root joint by aim constraining the tip
        to root joint and then parenting it under the root joint
        """
        # create the aim constraints between open and closed joints
        pm.aimConstraint(
            self._openWingJoint,
            self._openTipJoint,
            offset=(0,0,0),
            weight=1,
            aimVector=(0, 0, -self._direction),
            upVector=(0, -self._direction, 0),
            worldUpType='objectrotation',
            worldUpObject=self._openWingJoint,
            worldUpVector=(0,-self._direction,0)
        )
        
        pm.aimConstraint(
            self._closedWingJoint,
            self._closedTipJoint,
            offset=(0,0,0),
            weight=1,
            aimVector=(0, 0, -self._direction),
            upVector=(0, -self._direction, 0),
            worldUpType='objectrotation',
            worldUpObject=self._closedWingJoint,
            worldUpVector=(0,-self._direction,0)
        )
        
        # parent them to their roots
        pm.parent( self._openTipJoint, self._openWingJoint)
        pm.parent( self._closedTipJoint, self._closedWingJoint )
        pm.parent( self._tipJoint, self._rootJoint )
        
        # create axial correction groups for the tip joints
        oyAux.createAxialCorrectionGroup( self._openTipJoint )
        oyAux.createAxialCorrectionGroup( self._closedTipJoint )
        oyAux.createAxialCorrectionGroup( self._tipJoint )
    
    
    
    #----------------------------------------------------------------------
    def createFeatherBlenders(self):
        """creates blend nodes to blend guide feathers attributes over
        the other feathers
        """
        
        # get all the deformers
        bend1 = []
        bend2 = []
        twist = []
        
        for i in range(self._featherCount):
            bend1.append( self._feathers[i].bend1[0] )
            bend2.append( self._feathers[i].bend2[0] )
            twist.append( self._feathers[i].twist[0] )
        
        # create blender for bend1
        Utility.groupBlendAttributes( bend1, 'curvature' )
        Utility.groupBlendAttributes( bend2, 'curvature' )
        Utility.groupBlendAttributes( twist, 'startAngle' )
        Utility.groupBlendAttributes( twist, 'endAngle' )
    
    
    
    ##----------------------------------------------------------------------
    #def createFeatherBlenders(self):
        #"""creates blendTwoAttribute nodes between the first and last feather
        #of the current WingPart object
        #"""
        
        #for i in range(self._featherCount):
            ## the weight of

    def get_featherLength(self):
        """the getter for the featherLength attribute
        """
        return self._featherLength

    def set_featherLength(self, featherLength):
        """sets the featherLength attribute to the given featherLength value
        """
        self._featherLength = featherLength
    
    featherLength = property(get_featherLength, set_featherLength)





########################################################################
class Feather(object):
    """A feather class that helps to setup feather objects
    """
    
    
    
    #----------------------------------------------------------------------
    def __init__(self,
                 parentWingPart=None,
                 parentFollicle=None,
                 aimFollicle=None,
                 sourceObject=None,
                 upLocator=None,
                 aimLocator=None,
                 bend1=None,
                 bend2=None,
                 twist=None,
                 autoAlignTwist=None,
                 transformParent=None,
                 direction=1):
        
        self._bend1 = bend1
        self._bend2 = bend2
        self._twist = twist
        self._autoAlignTwist = autoAlignTwist
        
        self._parentWingPart = parentWingPart
        self._parentFollicle = parentFollicle
        
        self._aimFollicle = aimFollicle
        self._aimConstraintNode = None
        
        self._prevFeather = None
        self._nextFeather = None
        
        self._featherObject = None
        self._featherSourceObject = sourceObject
        
        self._upLocator = upLocator
        self._aimLocator = aimLocator
        
        self._isGuideFeather = False

        #print "Feather.__init__, transformParent: %s" % transformParent
        self._transformParent = transformParent
        
        self._direction = direction
        
        self._length = abs(self._direction)
    
    
    
    #----------------------------------------------------------------------
    def create(self):
        """creates the feather
        """

        #print "came here 1"
        
        # if there is no data do nothing
        if self._parentWingPart == None or self._parentFollicle == None or self._featherSourceObject == None or self._aimFollicle == None:
            return

        #print "came here 2"

        
        # duplicate the base object
        temp = self.featherSourceObject.duplicate(rc=True)
        #print "temp: %s" % temp
        
        self._featherObject = temp[0]
        
        if self._transformParent == None:
            pm.select(None)
            self._transformParent = pm.group(n='transformParent#')
            
            # parent the transformParent under the follicle
            pm.parent(self._transformParent, self._parentFollicle, r=True)
        
        # parent the feather to transform parent
        #print "self._featherObject   : %s" % self._featherObject
        #print "self._transformParent : %s" % self._transformParent
        
        pm.parent(self._featherObject, self._transformParent, r=True)
        
        # rotate the feather if the direction is -1
        if self._direction == -1:
            self._featherObject.setAttr('rz',180)
        
        # create up locator
        if self._upLocator == None:
            self.createUpLocator()
        
        if self._aimLocator == None:
            self.createAimLocator()
        
        # create aim constraint
        self.createAimConstraint()
        
        # create the deformers
        self.createDeformers()
    
    
    
    #----------------------------------------------------------------------
    def createAimConstraint(self):
        """creates the aim constraint
        """
        if self._parentFollicle == None or self._aimLocator == None or self._transformParent == None:
            return
        
        # create the aim constraint
        self.aimConstraintNode = pm.aimConstraint( self._aimLocator, self._transformParent, offset=(0,0,0), weight=1, aimVector=(0,0,1), upVector=(0,1,0), worldUpType='object', worldUpObject=self._upLocator, worldUpVector=(0,0,1) )
    
    
    
    #----------------------------------------------------------------------
    def createUpLocator(self):
        """creates the up vector locator
        """
        
        self._upLocator = pm.spaceLocator()
        
        self._upLocator.getShape().setAttr('localScale',(0.25,0.25,0.25))
        
        # parent it to the parent follicle
        pm.parent( self._upLocator, self._parentFollicle, r=True )
        pm.select(None)
        #pm.parent( self._upLocator, self._parentWingPart.getRootJoint(), r=False )
        
        # move it a little bit up
        #self._upLocator.setAttr('tz',1)
        pm.move(self._upLocator,(0,0,1),os=True,r=True)
        
        # create an axial correction group for the up locator
        oyAux.createAxialCorrectionGroup(self._upLocator)
    
    
    
    #----------------------------------------------------------------------
    def createAimLocator(self):
        """creates the aim vector locator
        """
        
        self._aimLocator = pm.spaceLocator()
        
        self._aimLocator.getShape().setAttr('localScale',(0.25,0.25,0.25))
        
        # parent it to the aim follicle
        pm.select(None)
        pm.parent( self._aimLocator, self._aimFollicle, r=True )
    
    
    
    #----------------------------------------------------------------------
    def createDeformers(self):
        """creates the feather deformers, which are two bend and one
        twist deformer
        """
        
        #[0] - the deformer
        #[1] - the handle
        # bend1
        pm.select(None)
        if self._bend1 == None:
            bend1 = pm.nonLinear( self._featherObject.getShape(), type='bend', defaultScale=True)
            # parent it under self._transformParent
            pm.parent( bend1[1], self._transformParent )
            #adjust settings and position
            bend1[0].setAttr('lowBound',-10)
            bend1[0].setAttr('highBound',10)
            bend1[1].setAttr('t',(0,0,0))
            bend1[1].setAttr('r',(0,0,90))
            self._bend1 = bend1
        else:
            # append the object to the given bend deformer
            pm.nonLinear( self._bend1[0], edit=True, geometry=self._featherObject.getShape() )
        
        # twist
        pm.select(None)
        if self._twist == None:
            twist = pm.nonLinear( self._featherObject.getShape(), type='twist', defaultScale=True)
            # parent it under self._transformParent
            pm.parent( twist[1], self._transformParent )
            #adjust settings and position
            twist[0].setAttr('lowBound', 0)
            twist[0].setAttr('highBound', 10)
            twist[0].setAttr('startAngle', 0)
            twist[1].setAttr('t', (0,0,0))
            twist[1].setAttr('r', (90,0,0))
            self._twist = twist
        else:
            # append the object to the given twist deformer
            pm.nonLinear( self._twist[0], edit=True, geometry=self._featherObject.getShape() )
        
        # autoAlignTwist
        # TODO: Check this
        pm.select(None)
        if self._autoAlignTwist == None:
            print "self._length: %f" % self._length
            assert(isinstance(self._parentWingPart, WingPart))
            self._autoAlignTwist = Utility.smartTwist(
                self._featherObject,
                None,
                self._parentWingPart._tipSurface,
                self._length
            )
        else:
            # append the object to the given auto align twist deformer
            pm.nonLinear( self._autoAlignTwist[0], edit=True, geometry=self._featherObject.getShape() )
        
        # bend2
        pm.select(None)
        if self._bend2 == None:
            bend2 = pm.nonLinear( self._featherObject.getShape(), type='bend', defaultScale=True)
            # parent it under self._transformParent
            pm.parent( bend2[1], self._transformParent )
            #adjust settings and position
            bend2[0].setAttr('lowBound',0)
            bend2[0].setAttr('highBound',10)
            bend2[1].setAttr('t',(0,0,0))
            bend2[1].setAttr('r',(90,0,90))
            self._bend2 = bend2
        else:
            # append the object to the given bend deformer
            pm.nonLinear( self._bend2[0], edit=True, geometry=self._featherObject.getShape() )
    
    
    
    #----------------------------------------------------------------------
    def getParentFollicle(self):
        """get the parent follicles transform node
        """
        return self._parentFollicle
    
    
    
    #----------------------------------------------------------------------
    def setParentFollicle(self, parentFollicle):
        """set the parent follicles transform node
        """
        # TODO : parent to the new follicles transfrom node and zero out
        # transformation values
        self._parentFollicle = parentFollicle
    
    parentFollicle = property( getParentFollicle, setParentFollicle )
    
    
    #----------------------------------------------------------------------
    def getAimFollicle(self):
        """get the aim follicles transform node
        """
        return self._aimFollicle
    
    
    
    #----------------------------------------------------------------------
    def setAimFollicle(self, aimFollicle):
        """set the aim follicles transform node
        """
        # TODO : delete the current aimConstraint and recreate a new one
        self._aimFollicle = aimFollicle
    
    aimFollicle = property( getAimFollicle, setAimFollicle )
    
    
    
    #----------------------------------------------------------------------
    def getAimLocator(self):
        """gets the aim locator
        """
        return self._aimLocator
    
    
    
    #----------------------------------------------------------------------
    def setAimLocator(self, aimLocator):
        """sets the aim locator
        """
        
        self._aimLocator = aimLocator
    
    aimLocator = property( getAimLocator, setAimLocator )
    
    
    
    #----------------------------------------------------------------------
    def getAimConstraintNode(self):
        """get the aim constraint node
        """
        return self._aimConstraintNode
    
    
    
    #----------------------------------------------------------------------
    def setAimConstraintNode(self, aimConstraintNode):
        """set the aimConstraint node
        """
        # do nothing just store the node
        self._aimConstraintNode = aimConstraintNode
    
    aimConstraintNode = property( getAimConstraintNode, setAimConstraintNode )
    
    
    
    #----------------------------------------------------------------------
    def getFeatherObject(self):
        """get the featherObject
        """
        return self._featherObject
    
    
    
    #----------------------------------------------------------------------
    def setFeatherObject(self, featherObject):
        """set the featherObject
        """
        self._featherObject = featherObject
    
    featherObject = property( getFeatherObject, setFeatherObject )
    
    
    #----------------------------------------------------------------------
    def getFeatherSourceObject(self):
        """get the featherSourceObject
        """
        return self._featherSourceObject
    
    
    
    #----------------------------------------------------------------------
    def setFeatherSourceObject(self, featherSourceObject):
        """set the featherSourceObject
        """
        # TODO: replace the old object with deformers
        self._featherSourceObject = featherSourceObject
    
    featherSourceObject = property( getFeatherSourceObject, setFeatherSourceObject )
    
    
    #----------------------------------------------------------------------
    def getIsGuideFeather(self):
        """get the feather a guide feather
        """
        return self._isGuideFeather
    
    
    
    #----------------------------------------------------------------------
    def setIsGuideFeather(self, isGuideFeather):
        """set the feather a guide feather
        """
        # TODO: make the neccessary connections to let it guide the surrounding
        # feathers
        # go to the next and previous feathers until you find another guide
        # and make the connections to blend the attributes
        self.isGuideFeather = isGuideFeather
    
    isGuideFeather = property( getIsGuideFeather, setIsGuideFeather )
    
    
    #----------------------------------------------------------------------
    def getLength(self):
        """get the feather length
        """
        return self._length
    
    
    
    #----------------------------------------------------------------------
    def setLength(self, length):
        """set the feather length
        """
        # TODO : resize the objectand the deformers
        self._length = length
    
    length = property( getLength, setLength )
    
    
    
    #----------------------------------------------------------------------
    def getBend1(self):
        """get the bend1
        """
        return self._bend1
    
    
    #----------------------------------------------------------------------
    def setBend1(self, bend1):
        """sets the bend1 deformer
        """
        # TODO: adjust the bend handle to make it align to the x-axis of the
        # feather object
        self._bend1 = bend1
    
    bend1 = property( getBend1, setBend1 )
    
    
    
    #----------------------------------------------------------------------
    def getBend2(self):
        """get the bend2
        """
        return self._bend2
    
    
    #----------------------------------------------------------------------
    def setBend2(self, bend2):
        """sets the bend2 deformer
        """
        # TODO: adjust the bend handle to make it align to the z-axis of the
        # feather object
        self.bend2 = bend2
    
    bend2 = property( getBend2, setBend2 )
    
    
    
    #----------------------------------------------------------------------
    def getDirection(self):
        """get the direction
        """
        return self._direction
    
    
    
    #----------------------------------------------------------------------
    def setDirection(self, direction):
        """set the direction
        """
        self._direction = direction
        
        ## rotate the object if the direction is -1
        #if self._direction == -1:
            #self._featherObject.setAttr('rz',180)
    
    direction = property( getDirection, setDirection )
    
    
    
    #----------------------------------------------------------------------
    def getTwist(self):
        """get the twist deformer
        """
        return self._twist
    
    
    
    #----------------------------------------------------------------------
    def setTwist(self, twist):
        """set the twist deformer
        """
        # TODO : adjust the twist handle to make it align to the z-axis of the
        # feather object
        self._twist = twist
    
    twist = property( getTwist, setTwist )


    #----------------------------------------------------------------------
    def getAutoAlignTwist(self):
        """get the autoAlignTwist deformer
        """
        return self._autoAlignTwist
    
    
    
    #----------------------------------------------------------------------
    def setAutoAlignTwist(self, autoAlignTwist):
        """set the twist deformer
        """
        self._autoAlignTwist = autoAlignTwist
    
    autoAlignTwist = property( getAutoAlignTwist, setAutoAlignTwist )
    
    
    
    #----------------------------------------------------------------------
    def getUpLocator(self):
        """get the upLocator
        """
        return self._upLocator
    
    
    
    #----------------------------------------------------------------------
    def setUpLocator(self, upLocator):
        """sets the upLocator
        """
        
        # TODO: check if the old upLocator should be deleted or not
        
        self._upLocator = upLocator
        
        if self._aimConstraintNode != None:
            self._upLocator.attr('worldMatrix[0]') >> self._aimConstraintNode.attr('worldUpMatrix')
    
    upLocator = property( getUpLocator, setUpLocator )
    
    
    
    #----------------------------------------------------------------------
    def getTransformParent(self):
        """get the transform parent
        """
        return self._transformParent
    
    
    
    #----------------------------------------------------------------------
    def setTransformParent(self, transformParent):
        """set the transform parent
        """
        self._transformParent = transformParent
    
    transformParent = property( getTransformParent, setTransformParent )
    
    
    
    #----------------------------------------------------------------------
    def getParentWingPart(self):
        """get the parent wingPart object
        """
        return self._parentWingPart
    
    
    #----------------------------------------------------------------------
    def setParentWingPart(self, parentWingPart):
        """set the parent wingPart
        """
        self._parentWingPart = parentWingPart
    
    parentWingPart = property( getParentWingPart, setParentWingPart )
    
    
    
    #----------------------------------------------------------------------
    def getPrevFeather(self):
        """get the prevFeather
        """
        return self._prevFeather
    
    
    
    #----------------------------------------------------------------------
    def setPrevFeather(self, prevFeather):
        """set the prevFeather
        """
        self._prevFeather = prevFeather
    
    prevFeather = property( getPrevFeather, setPrevFeather )
    
    
    #----------------------------------------------------------------------
    def getNextFeather(self):
        """get the nextFeather
        """
        return self._nextFeather
    
    
    #----------------------------------------------------------------------
    def setNextFeather(self, nextFeather):
        """set the nextFeather
        """
        self._nextFeather = nextFeather
    
    nextFeather = property( getNextFeather, setNextFeather )






########################################################################
class Utility(object):
    """its the utility class that has the basic functions
    """
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def attachToCurve(obj, curve, param=0.0, parametricLength=True):
        """attaches the given object to the given curve by using a motion path
        
        returns the motion path name
        """
        
        # create the motionPath node
        motionPath = pm.createNode('motionPath')
        
        # connect the curves world space to the motion path
        curveShape = curve.getShape()
        curveShape.attr('worldSpace[0]') >> motionPath.attr('geometryPath')
        
        # connect motionPath.message to obj.specifiedManipLocation
        motionPath.attr('message') >> obj.attr('specifiedManipLocation')
        
        # create three addDoubleLinear nodes
        addDoubleLinear1 = pm.createNode('addDoubleLinear')
        addDoubleLinear2 = pm.createNode('addDoubleLinear')
        addDoubleLinear3 = pm.createNode('addDoubleLinear')
        
        # create a vector product to correct parent transforms
        vectorProd = pm.createNode('vectorProduct')
        
        # connect obj.transMinusRotatePivotX to addDoubleLinear1.input1
        # connect motionPath.xCoordinate to addDoubleLinear1.input2
        obj.attr('transMinusRotatePivotX') >> addDoubleLinear1.attr('input1')
        motionPath.attr('xCoordinate') >> addDoubleLinear1.attr('input2')
        addDoubleLinear1.attr('output') >> vectorProd.attr('input1X')
        
        # connect obj.transMinusRotatePivotY to addDoubleLinear2.input1
        # connect motionPath.yCoordinate to addDoubleLinear2.input2
        obj.attr('transMinusRotatePivotY') >> addDoubleLinear2.attr('input1')
        motionPath.attr('yCoordinate') >> addDoubleLinear2.attr('input2')
        addDoubleLinear2.attr('output') >> vectorProd.attr('input1Y')
        
        # connect obj.transMinusRotatePivotZ to addDoubleLinear3.input1
        # connect motionPath.zCoordinate to addDoubleLinear3.input2
        obj.attr('transMinusRotatePivotZ') >> addDoubleLinear3.attr('input1')
        motionPath.attr('zCoordinate') >> addDoubleLinear3.attr('input2')
        addDoubleLinear3.attr('output') >> vectorProd.attr('input1Z')
        
        # connect the parentInverseMatrix of the obj to vectorProd
        obj.attr('parentInverseMatrix') >> vectorProd.attr('matrix')
        
        # set the operation to point matrix multiplication
        vectorProd.setAttr('operation', 4 )
        
        # connect vectorProd back to the obj
        vectorProd.attr('output') >> obj.attr('t')
        
        # set the motion path parametric length to false ( or the fraction mode
        # to True)
        # and set the param as uValue
        
        motionPath.setAttr('fractionMode', parametricLength)
        motionPath.setAttr('uValue', param)
        
        return motionPath
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def attachToCurve2(obj, curve, param=0.0):
        """attaches the given obj to the given curve by using pointOnCurveInfo
        """
        curveShape = curve.getShape()
        
        pOCI = pm.createNode('pointOnCurveInfo')
        curveShape.attr('worldSpace[0]') >> pOCI.attr('inputCurve')
        
        vectorProd = pm.createNode('vectorProduct')
        obj.attr('parentInverseMatrix[0]') >> vectorProd.attr('matrix')
        
        pOCI.setAttr('turnOnPercentage', 1)
        pOCI.setAttr('parameter', param)
        vectorProd.setAttr('operation',4) # point matrix product
        pOCI.attr('result.position') >> vectorProd.attr('input1')
        vectorProd.attr('output') >> obj.attr('t')
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def circumCurvature( node0, node1, node2):
        """creates a rig that calculates the circum radius of given three
        points
        """
        
        a = pm.createNode('distanceBetween')
        b = pm.createNode('distanceBetween')
        c = pm.createNode('distanceBetween')
        
        # edge0
        node1.attr('worldMatrix[0]') >> a.attr('inMatrix1')
        node0.attr('worldMatrix[0]') >> a.attr('inMatrix2')
        
        # edge1
        node2.attr('worldMatrix[0]') >> b.attr('inMatrix1')
        node1.attr('worldMatrix[0]') >> b.attr('inMatrix2')
        
        #edge2
        node0.attr('worldMatrix[0]') >> c.attr('inMatrix1')
        node2.attr('worldMatrix[0]') >> c.attr('inMatrix2')
        
        mult1 = pm.createNode('multDoubleLinear')
        axbxc = pm.createNode('multDoubleLinear')
        
        a.attr('d') >> mult1.attr('input1')
        b.attr('d') >> mult1.attr('input2')
        mult1.attr('o') >> axbxc.attr('input1')
        c.attr('d') >> axbxc.attr('input2')
        
        # minus_a
        minus_a = pm.createNode('multDoubleLinear')
        a.attr('d') >> minus_a.attr('input1')
        minus_a.setAttr('input2', -1.0)
        
        # minus_b
        minus_b = pm.createNode('multDoubleLinear')
        b.attr('d') >> minus_b.attr('input1')
        minus_b.setAttr('input2', -1.0)
        
        # minus_c
        minus_c = pm.createNode('multDoubleLinear')
        c.attr('d') >> minus_c.attr('input1')
        minus_c.setAttr('input2', -1.0)
        
        # a+b-c
        ab_c = pm.createNode('plusMinusAverage')
        a.attr('d') >> ab_c.attr('input1D[0]')
        b.attr('d') >> ab_c.attr('input1D[1]')
        minus_c.attr('o') >> ab_c.attr('input1D[2]')
        
        # a+c-b
        ac_b = pm.createNode('plusMinusAverage')
        a.attr('d') >> ac_b.attr('input1D[0]')
        c.attr('d') >> ac_b.attr('input1D[1]')
        minus_b.attr('o') >> ac_b.attr('input1D[2]')
        
        # b+c-a
        bc_a = pm.createNode('plusMinusAverage')
        b.attr('d') >> bc_a.attr('input1D[0]')
        c.attr('d') >> bc_a.attr('input1D[1]')
        minus_a.attr('o') >> bc_a.attr('input1D[2]')
        
        # a+b+c
        abc = pm.createNode('plusMinusAverage')
        a.attr('d') >> abc.attr('input1D[0]')
        b.attr('d') >> abc.attr('input1D[1]')
        c.attr('d') >> abc.attr('input1D[2]')
        
        # (a+b+c) * (b+c-a)
        mult2 = pm.createNode('multDoubleLinear')
        abc.attr('output1D') >> mult2.attr('input1')
        bc_a.attr('output1D') >> mult2.attr('input2')
        
        # (a+c-b) * (a+b-c)
        mult3 = pm.createNode('multDoubleLinear')
        ac_b.attr('output1D') >> mult3.attr('input1')
        ab_c.attr('output1D') >> mult3.attr('input2')
        
        # all togather
        mult4 = pm.createNode('multDoubleLinear')
        mult2.attr('o') >> mult4.attr('input1')
        mult3.attr('o') >> mult4.attr('input2')
        
        # sqrt
        sqrt = pm.createNode('multiplyDivide')
        mult4.attr('o') >> sqrt.attr('input1X')
        sqrt.setAttr('input2X', 0.5)
        sqrt.setAttr('operation', 3)
        
        # a*b*c / sqrt is the radius
        # sqrt / a*b*c is the curvature
        circumCurvature = pm.createNode('multiplyDivide')
        sqrt.attr('outputX') >> circumCurvature.attr('input1X')
        axbxc.attr('o') >> circumCurvature.attr('input2X')
        circumCurvature.setAttr('operation', 2 )
        
        return circumCurvature
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def closestPointOnSurface(obj, surface):
        """returns the closest point on the given surface for the given object
        """
        
        sourceTra = pm.xform( obj, q=True, ws=True, t=True)
        
        return Utility.closestPointOnSurfaceToPoint( sourceTra, surface )
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def closestPointOnSurfaceToPoint(point, surface):
        """"""
        
        cPOM = pm.createNode('closestPointOnSurface')
        
        surfaceShape = surface.getShape()
        
        pm.connectAttr( surfaceShape.attr('worldSpace[0]'), cPOM.attr('is') )
        cPOM.setAttr( 'ip', point)
        
        outTra = cPOM.getAttr('r.p')
        outU = cPOM.getAttr('r.u')
        outV = cPOM.getAttr('r.v')
        
        # delete the cPOM node
        pm.disconnectAttr( surfaceShape.attr('worldSpace[0]'), cPOM.attr('is') )
        pm.delete( cPOM )
        
        return ( outTra, outU, outV )
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def connectToNode(node1, attr1, node2, attr2):
        """connects the animation curve that is connected to node1.attr1 to 
        node2.attr2
        """
        
        animCurveNode = Utility.getAnimationCurve( node1, attr1 )
        
        if animCurveNode != None:
            node2.attr(attr2) >> animCurveNode.attr('input')
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def connectToTime( node, attribute ):
        """connects the animation curve that is connected to the given nodes
        given attribute to time
        """
        
        # get the animation curve
        animCurve = Utility.getAnimationCurve( node, attr )
        timeNode = Utility.getTimeNode()
        
        if animCurve != None:
            
            # store the input before connecting to the time node
            animInput = animCurve.attr('input').inputs[0]
            
            timeNode.attr('outTime') >> animCurve.attr('input')
            
            # if there is any unit conversion node delete them
            if animInput.type() == 'unitToTimeConversion':
                pm.delete( animInput )
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def convertToHair(curve, follicle, hairSystem, parentToFollicle=True):
        """converts the given curve to hair curve by using the follicle and
        hairSystem
        
        returns the hairCurves transform node
        """
        
        # duplicate the given curve under the world
        temp = pm.duplicate(curve)
        dynamicCurve = temp[0]
        
        if dynamicCurve.getParent() is not None:
            pm.parent( dynamicCurve, w=1 )
        
        # set dynamic curves transform to 0
        oyAux.goHome( dynamicCurve )
        
        # get the shapes
        curveShape = curve.getShape()
        dynamicCurveShape = dynamicCurve.getShape()
        follicleShape = follicle.getShape()
        hairSystemShape = hairSystem.getShape()
        
        # connect the dynamic curve to the follicle
        curveShape.attr('worldSpace[0]') >> follicleShape.attr('startPosition')
        follicleShape.attr('outCurve') >> dynamicCurveShape.attr('create')
        
        # connect to the hairSystem
        index = hairSystemShape.attr('inputHair').numElements()
        follicleShape.attr('outHair') >> hairSystemShape.attr('inputHair[' + str(index) +']')
        hairSystemShape.attr('outputHair[' + str(index) + ']') >> follicleShape.attr('currentPosition')
        
        # set the follicle to dynamic
        follicleShape.setAttr('simulationMethod', 2)
        
        # and set it to match the start curve
        follicleShape.setAttr('startDirection', 1 )
        
        # set it to the same degree with the input curve
        follicleShape.setAttr('degree', curveShape.getAttr('degree') )
        
        # parent the base curve under the follicle
        if parentToFollicle:
            pm.parent( curve, follicle )
        
        return dynamicCurve
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createAttributeBlender(obj1, attr1, obj2, attr2, weight=0.0):
        """creates an attribute blender between the given objects and
        attributes
        
        returns the attribute blender node
        """
        
        attrBlender = pm.createNode('blendTwoAttr')
        
        obj1.attr(attr1) >> attrBlender.attr('input[0]')
        obj2.attr(attr2) >> attrBlender.attr('input[1]')
        
        attrBlender.setAttr('ab',weight)
        
        return attrBlender
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createCurve(cvPosList, useAsEditPoints=False ):
        """creates a curve with the cvPos list
        
        if useAsEditPoints is True then the given coordinates are used as edit
        point coordinates
        """
        
        if useAsEditPoints:
            curvePath = pm.curve( d=3, ep=cvPosList )
        else:
            curvePath = pm.curve( d=3, p=cvPosList )
        
        curveNode = pm.PyNode( curvePath )
        return curveNode
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createCurveFromObjs(objList, useAsEditPoints=False):
        """creates a new curve from the given objects list
        """
        posList = [ pm.xform(node, q=1, ws=1, t=1) for node in objList ]
        return Utility.createCurve(posList, useAsEditPoints)
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createCurveWithStartEndPosition( startPos, endPos, cvCount ):
        """creates a third degree curve between the start and end positions
        with givent cvCount
        """
        startPosV = pm.util.VectorN(startPos)
        endPosV = pm.util.VectorN(endPos)
        totalDiff = endPosV - startPosV
        unitDiff = totalDiff / float(cvCount - 1)
        
        cvPosList = []
        cvPosList.append( startPos )
        
        # calculate the inner CV positions
        for i in range(cvCount):
            currentV = startPosV + unitDiff * float(i)
            #assert(isinstance(currentV, pm.util.VectorN))
            cvPosList.append( currentV )
        
        return Utility.createCurve( cvPosList )
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createFollicle(surface, u, v):
        """creates a hair follicle and connects it to the given surface at
        given coordinate
        
        returns the transform node of the created follicle
        """
        
        follicleShape = pm.createNode('follicle')
        
        surfaceShape = surface.getShape()
        
        surfaceShape.attr('local') >> follicleShape.attr('inputSurface')
        surfaceShape.attr('worldMatrix[0]') >> follicleShape.attr('inputWorldMatrix')
        
        ## get span counts
        #uvSpanCnt = surface.getAttr('spansUV')
        
        # get minMaxRangeU and minMaxRangeV
        minMaxRangeU = surfaceShape.getAttr('minMaxRangeU')
        minMaxRangeV = surfaceShape.getAttr('minMaxRangeV')
        
        # set uv values
        follicleShape.setAttr ('pu', u/(minMaxRangeU[1]-minMaxRangeU[0]) )
        #follicleShape.setAttr ('pv', v/float(uvSpanCnt[1]))
        follicleShape.setAttr ('pv', v/(minMaxRangeV[1]-minMaxRangeV[0]) )
        
        # set simulation to static
        follicleShape.setAttr ('simulationMethod', 0)
        
        # connect to its transform node
        follicle = follicleShape.getParent()
        
        follicleShape.attr('outTranslate') >> follicle.attr('t')
        follicleShape.attr('outRotate') >> follicle.attr('r')
        
        return follicle
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createHairSystem():
        """creates an empty hair system and returns the transform node of
        the created hairSystem
        """
        
        hairSystemShape = pm.createNode('hairSystem')
        hairSystem = hairSystemShape.getParent()
        
        # connect it to the time1 node
        timeNode = pm.PyNode('time1')
        
        timeNode.attr('outTime') >> hairSystemShape.attr('currentTime')
        
        # just return the transform node
        return hairSystem
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createProjection(
        node,
        projectionPlaneLocalNormal=(0,1,0),
        projectionPlaneObject=None,
        projectionPlaneOffset=(0,0,0)
        ):
        """creates a projection of the given object in the given local space of
        another object
        
        projectionPlaneNormal defines the plane that the target is going to
        fixed to
        
        if projectionPlaneObject is also defined, the projectionPlaneNormal
        becomes the local axis of the given projectionPlaneObject
        """
        
        # create a locator (projectionLocator)
        # parent it under the projectionPlaneObject
        # zero its transformations out
        # create an axial correction for it
        
        # create another locator (upLocator)
        # parent it under projectionPlaneObject
        # zero its transformations out
        # move it to the projectionPlaneLocalNormal
        
        # aim constraint the axialCorrectionGroups +Y axis to it
        # delete the aimConstraint and upLocator
        # point constraint the node to the projectionLocator in X and Z    
        
        # ******************
        # projection locator
        projectionLocator = pm.spaceLocator()
        
        # rename it:
        projectionLocator.rename( node.name() + '_projectionLocator#' )
        
        pm.parent( projectionLocator, projectionPlaneObject, r=True )
        projectionLocatorACG = oyAux.createAxialCorrectionGroup( projectionLocator )
        
        # ******************
        # up locator
        upLocator = pm.spaceLocator()
        
        # rename it:
        upLocator.rename( node.name() + '_projectionUpLoator#' )
        
        pm.parent( upLocator, projectionPlaneObject, r=True )
        upLocator.setAttr('t', projectionPlaneLocalNormal )
        
        pm.delete( pm.aimConstraint( upLocator,
                          projectionLocatorACG,
                          mo=False,
                          w=1,
                          aimVector=(0,1,0),
                          upVector=(0,0,1),
                          worldUpType='objectrotation',
                          worldUpObject=projectionPlaneObject
                          )
                   , upLocator )
        
        pm.pointConstraint( node, projectionLocator, skip='y' )
        
        # offset the projectLocatorACG
        pm.move(projectionLocatorACG, projectionPlaneOffset, relative=1, localSpace=1 )
        
        return projectionLocator
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createSplineIK(startJoint, endJoint):
        """creates spline IK between given joints
        with advanced twist controls
        """
        # get the joint list
        jointList = Utility.getJointHierarchy( startJoint, endJoint )
        
        # create the curve between joints
        curve = Utility.createCurveFromObjs( jointList, True )
        
        ikHandle = pm.ikHandle( startJoint=startJoint,
                                endEffector=endJoint,
                                curve=curve,
                                solver='ikSplineSolver',
                                twistType='linear',
                                simplifyCurve=False,
                                snapCurve=False,
                                createCurve=False)
        
        # create twist locators and place them to the correct placement
        startTwistLocator = pm.spaceLocator()
        endTwistLocator = pm.spaceLocator()
        
        pm.parent( startTwistLocator, startJoint )
        oyAux.goHome( startTwistLocator )
        startTwistLocator.setAttr('ty', 1)
        pm.parent( startTwistLocator, w=1 )
        
        pm.parent( endTwistLocator, endJoint )
        oyAux.goHome( endTwistLocator )
        endTwistLocator.setAttr('ty', 1)
        pm.parent( endTwistLocator, w=1 )
        
        # set ikHandle advanced twist properties
        ikHandle[0].setAttr('dTwistControlEnable', 1);
        ikHandle[0].setAttr( 'dWorldUpType', 2 )
        
        startTwistLocator.attr('worldMatrix[0]') >> \
                         ikHandle[0].attr('dWorldUpMatrix')
        
        endTwistLocator.attr('worldMatrix[0]') >> \
                       ikHandle[0].attr('dWorldUpMatrixEnd')
        
        
        return ikHandle
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createSurfaceFromCurve(curve, direction, scale=1.0, history=False):
        """creates a new nurbs surface by first offsetting the curve twice,
        and then creating a loft surface between them
        
        direction should be a tupple of 3 floats
        """
        
        # calculate the normal
        normal = Utility.getLocalVector( curve, direction )
        
        # create the offset curves
        temp = pm.offsetCurve( curve, d=(0.5 * scale), nr=normal, ch=history, sd=0 )
        profileCurve1 = temp[0]
        
        temp = pm.offsetCurve( curve, d=-(0.5 * scale), nr=normal, ch=history, sd=0 )
        profileCurve2 = temp[0]
        
        loftSurface = Utility.createSurfaceFromCurves( [profileCurve1, profileCurve2] )
        
        return loftSurface, profileCurve1, profileCurve2
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createSurfaceFromCurves(curves, history=False):
        """creates a surfrace from the given curves
        """
        
        # create the surface
        temp = pm.loft( curves, ar=False, ch=history )
        
        if isinstance(temp, pm.nodetypes.Transform):
            loftSurface = temp
        elif isinstance(temp, list):
            loftSurface = temp[0]
        
        #if not history:
            #pm.delete( profileCurve1, profileCurve2 )
        
        return loftSurface
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def createThreePointArc(node1, node2, node3, history=True):
        """creates a three point arc node from the given nodes
        """
        
        tPA = pm.createNode ('makeThreePointCircularArc')
        
        if history:
            # load decomposeMatrix
            if not pm.pluginInfo ( 'decomposeMatrix', q=True, loaded=True ):
                pm.loadPlugin( 'decomposeMatrix')
            
            dCM1 = pm.createNode('decomposeMatrix')
            dCM2 = pm.createNode('decomposeMatrix')
            dCM3 = pm.createNode('decomposeMatrix')
            
            node1.attr('worldMatrix[0]') >> dCM1.attr('inputMatrix')
            node2.attr('worldMatrix[0]') >> dCM2.attr('inputMatrix')
            node3.attr('worldMatrix[0]') >> dCM3.attr('inputMatrix')
            
            dCM1.attr('outputTranslate') >> tPA.attr('point1')
            dCM2.attr('outputTranslate') >> tPA.attr('point2')
            dCM3.attr('outputTranslate') >> tPA.attr('point3')
        else:
            point1 = pm.xform( node1, q=1, ws=1, t=1 )
            point2 = pm.xform( node2, q=1, ws=1, t=1 )
            point3 = pm.xform( node3, q=1, ws=1, t=1 )
            
            tPA.setAttr('point1', point1)
            tPA.setAttr('point2', point2)
            tPA.setAttr('point3', point3)
        
        return tPA
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def duplicateJointChain(startJoint):
        """duplicates the joint chain by starting from the given joint
        """
        
        assert(isinstance(startJoint, pm.nodetypes.Joint))
        
        # just duplicate the joint
        dupJointList = startJoint.duplicate(rc=True)
        
        currentJoint = dupJointList[0]
        children = currentJoint.getChildren()
        
        while ( len(children) > 0 ):
            
            if len(children) > 1:
                # there should be more than one child delete the others
                pm.delete( children[1:] )
            
            currentJoint = children[0]
            children = currentJoint.getChildren()
        
        return dupJointList[0]
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def duplicateOverBone( joint, obj, count, startFromJoint=True ):
        """duplicates the given object over the bone
        """
        # get the first childs position
        if not Utility.hasChild(joint) or count<=1:
            return []
        
        objList = []
        
        assert(isinstance(joint, pm.nt.Joint))
        childJoint = joint.getChildren()[0]
        
        startPos = pm.xform( joint, q=1, ws=1, t=1 )
        endPos = pm.xform( childJoint, q=1, ws=1, t=1 )
        
        startPosV = pm.util.VectorN( startPos )
        endPosV = pm.util.VectorN( endPos )
        totalDiff = endPosV - startPosV
        
        
        if startFromJoint:
            start = 0
            #stepSize = totalDiff / float(count-1)
        else:
            start = 1
            count += 1
        
        stepSize = totalDiff / float(count-1)
        
        
        for i in range(start,count):
            dup = pm.duplicate( obj, rc=True )
            pm.xform( dup[0], ws=1, t= (startPosV + stepSize * float(i) ) )
            objList.append( dup[0] )
        
        return objList
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def duplicateOverJoint(joint, objToDuplicate, count):
        """duplicates the given objects along the x axis of the given joint
        
        returns the new objects as a list
        """
        
        obj = objToDuplicate
        
        if not Utility.hasChild(joint):
            #count = 1
            return []
        
        jointLength = Utility.getJointLength( joint )
        
        newObjects = []
        dupList = []
        
        interDist = Utility.interleaver( count+1, jointLength )
        
        for i in range(count):
            dupList = obj.duplicate(rc=True)
            
            # place the object to the root of the joint
            dupList = pm.parent( dupList[0], joint, r=True )
            dupObj = pm.PyNode( dupList[0] )
            
            # move it to the place
            # instead of aligning the last object to the end of the joint
            # it leaves a little empty space ot let the other joints feathers
            # fit in
            dupObj.setAttr('tx', float(i+1) * interDist )
            
            newObjects.append( dupObj )
        
        return newObjects
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def duplicateOverJoint2(joint, objToDuplicate, count):
        """duplicates the given objects along the x axis of the given joint,
        but it doesn't start from the begining of the joint, its like adding
        count+2 objects on the joint and returning the only the internal
        objects
        
        returns the new objects as a list
        """
        
        obj = objToDuplicate
        
        if not Utility.hasChild(joint):
            count = 1
        
        jointLength = Utility.getJointLength( joint )
        
        newObjects = []
        dupList = []
        
        interDist = Utility.interleaver( count+2, jointLength )
        
        for i in range(count):
            dupList = obj.duplicate(rc=True)
            
            # place the object to the root of the joint
            dupList = pm.parent( dupList[0], joint, r=True )
            dupObj = pm.PyNode( dupList[0] )
            
            # move it to the place instead of aligning the last object to the
            # end of the joint it leaves a little empty space ot let the other
            # joints feathers fit in
            dupObj.setAttr('tx', ( float(i) + 1.0 ) * interDist )
            
            newObjects.append( dupObj )
        
        return newObjects
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def extendJoint(joint, placement=1, direction=1, extendAmount=1.0):
        """creates a new joint from the given joint at the given direction
        
        placement defines where to place the joint
        placement=0 places it to the start of the joint
        placement=1 places it to the start of the child joint
        
        direction 1 is the default and extends towards the child
        direction -1 extends towards the negative child (means the negative
        direction when you use 1 )
        """
        
        # get the first child
        child = Utility.getChildOfType( joint, pm.nodetypes.Joint )
        
        # duplicate the original joint
        extendedJoint = pm.duplicate( joint )[0]
        
        assert(isinstance(extendedJoint, pm.nodetypes.Joint) )
        
        # delete all the children of the new joint
        pm.delete( extendedJoint.getChildren() )
        
        # parent it to the original joint
        pm.parent( extendedJoint, joint )
        
        # duplicate it self and parent the new joint to the extendedJoint
        extendedChild = pm.duplicate( extendedJoint )[0]
        
        pm.parent( extendedChild, extendedJoint )
        
        extendedChild.setAttr('tx', extendAmount )
        
        # move it to the original joints childs position
        if placement == 1:
            tra = pm.xform( child, q=1, ws=1, t=1 )
            pm.xform( extendedJoint, ws=1, t=tra )
            
        if direction == -1:
            extendedJoint.setAttr('ry', 180)
            pm.makeIdentity( extendedJoint, apply=1, r=1 )
        
        #pm.select( extendedJoint )
        
        return extendedJoint
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getAnimationCurve(node1, attr1):
        """returns the animation curve that is connected to node1.attr1
        """
        #assert(isinstance(node1, pm.nodetypes.Transform))
        
        returnNode = None
        
        inputs = node1.attr(attr1).inputs()
        
        
        if len(inputs) > 0:
            if isinstance( inputs[0], pm.nodetypes.AnimCurve ):
                returnNode = inputs[0]
        
        return returnNode
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getAttributeSign(node, attr):
        """returns the given nodes given attributes sign
        U for doubleLinear and double
        A for doubleAngle
        """
        
        nodeAttrType = node.attr( attr ).type()
        nodeAttrTypeSign = ''
        
        if nodeAttrType == 'doubleLinear' or nodeAttrType == 'double':
            nodeAttrTypeSign = 'U'
        elif nodeAttrType == 'doubleAngle':
            nodeAttrTypeSign = 'A'
        
        return nodeAttrTypeSign
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getChildOfType(node, childType ):
        """returns the first child that is an instance of childType or None
        if the child is not an instance of that type
        
        Examples:
        1- to get a child Mesh
        getChildOfType( node, pm.nodetypes.Transform, pm.nodetypes.Mesh )
        """
        
        shapeType = None
        
        if childType == pm.nodetypes.Camera or \
           childType == pm.nodetypes.CurveShape or \
           childType == pm.nodetypes.Locator or \
           childType == pm.nodetypes.Lattice or \
           childType == pm.nodetypes.Mesh or \
           childType == pm.nodetypes.NurbsCurve or \
           childType == pm.nodetypes.NurbsSurface or \
           childType == pm.nodetypes.Subdiv or \
           childType == pm.nodetypes.Particle:
            shapeType = childType
            childType = pm.nodetypes.Transform
        
        #assert(isinstance(joint, pm.nodetypes.Joint))
        
        children = node.getChildren()
        childOfType = None
        
        if len(children) > 0:
            # check if the child is in childType
            for child in children:
                if isinstance(child, childType):
                    if shapeType is not None:
                        # get the shape
                        shape = child.getShape()
                        if isinstance( shape, shapeType ):
                            childOfType = child
                            break
                    else:
                        childOfType = child
                        break
        
        return childOfType
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getJointHierarchy(startJoint, endJoint):
        """returns a group of joints from the start to the end joints
        """
        
        assert( isinstance(startJoint, pm.nodetypes.Joint))
        
        jointList = []
       
        jointList.append( startJoint )
        
        childJoint = Utility.getChildOfType( startJoint, pm.nodetypes.Joint )
        currentJoint = childJoint
        
        while ( currentJoint != endJoint and currentJoint is not None ):
            
            jointList.append( childJoint )
            childJoint = Utility.getChildOfType( currentJoint, pm.nodetypes.Joint )
            currentJoint = childJoint
        
        jointList.append( endJoint )
        return jointList
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getJointLength(joint):
        """returns the joint length
        """
        
        # get the first child and get the childs x position and return it as
        # the joint length
        
        childs = joint.listRelatives(c=True)
        
        if len(childs) < 1:
            return 0.0
        
        length = childs[0].getAttr('tx')
        
        return length
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getJointList(startJoint, jointCount):
        """returns a list that contains the joints that are nth children of the
        start joint
        """
        
        jointList = []
        jointList.append( startJoint )
        
        currentJoint = startJoint
        
        for i in range(jointCount-1):
            currentJoint = currentJoint.childAtIndex(0)
            jointList.append( currentJoint )
        
        return jointList
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def getLocalVector(obj, vector):
        """returns the vector transformed to the given objects space
        """
        
        vectorN = pm.util.VectorN( vector )
        matrixN = pm.util.MatrixN( obj.getAttr('worldMatrix[0]') )
        
        return vectorN * matrixN
    
    
    
    #----------------------------------------------------------------------
    def getTimeNode():
        """returns the default time node
        """
        return pm.ls('time1')[0]
        
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def groupBlendAttributes(objList, attrName):
        """blends the attributes of the given objects by weighting them
        between the first and the last in the list
        """
        
        if len(objList) < 1:
            return
        
        startObj = objList[0]
        endObj = objList[-1]
        
        objCount = len(objList)
        
        for i in range(1, objCount - 1 ):
            attrBlender = Utility.createAttributeBlender( startObj, attrName, endObj, attrName, weight = (float(i)/float(objCount-1)) )
            
            attrBlender.attr('output') >> objList[i].attr(attrName)
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def groupConstraint(objList, type='point'):
        """constraints the given objects to the objects in the each end of the
        list
        
        available constraint types are
        point
        orient
        parent
        scale
        """
        
        startObj = objList[0]
        endObj = objList[-1]
        
        if type == 'point':
            consCommand = pm.pointConstraint
        elif type == 'orient':
            consCommand = pm.orientConstraint
        elif type == 'parent':
            consCommand = pm.parentConstraint
        elif type == 'scale':
            consCommand = pm.scaleConstraint
        else:
            consCommand = pm.pointConstraint
        
        objCount = len(objList)
        
        for i in range(1, objCount - 1):
            consCommand( startObj, objList[i], w=(objCount-1-i)  )
            consCommand( endObj, objList[i], w=i )
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def hasChild(joint):
        """returns True if the joint has childs
        """
        return len(joint.listRelatives(c=True)) > 0
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def interleaver(count, distance):
        """calculates the neccesary distance between elements to fit them to
        the given distance
        """
        
        if count > 1:
            return (float(distance) / float(count-1))
        else:
            return 0.0
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def prefixNodeName(node, prefix):
        """adds the given prefix to the given node name
        """
        
        #assert(isinstance(node, pm.core.nodetypes.DependNode))
        node.rename( prefix + "_" + node.name() )
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def smartBend( node, direction=[1,0,0] ):
        """creates a bend rig which is controlled easier than a normal bend
        deformer
        """
        
        # create the rigs root transform node
        xform = pm.group( em=True )
        
        # ********************
        # The Locators
        # ********************
        
        # Second Point Locator
        secondPointLoc = pm.spaceLocator()
        pm.parent( secondPointLoc, xform)
        oyAux.goHome( secondPointLoc )
        #secondPointLoc.setAttr('ty',0.01)
        secondPointLoc.setAttr('v', 0)
        
        # The Local representation of the controller
        localThirdpoint = pm.spaceLocator()
        pm.parent( localThirdpoint, xform )
        localThirdpoint.setAttr('v', 0)
        
        # The Controller
        controller = pm.spaceLocator()
        pm.parent( controller, xform )
        controller.setAttr('ty',1)
        
        # point constraint the localThirdPoint to controller
        pm.pointConstraint( controller, localThirdpoint, w=1 )
        
        # set the secondPoint to 0.01 of the thirdPoint
        divider = pm.createNode( 'multDoubleLinear')
        divider.setAttr('input2', 0.01)
        localThirdpoint.attr('ty') >> divider.attr('input1')
        divider.attr('o') >> secondPointLoc.attr('ty')
        
        
        # ******************
        # Bend
        # ********************
        bend = pm.nonLinear( node, type='bend', defaultScale=True )
        # parent it under xform
        pm.parent( bend[1], xform )
        oyAux.goHome( bend[1] )
        
        
        # create a projection of the controller to the xform
        projectionLoc = Utility.createProjection( controller, (0,1,0), xform )
        
        # aim the bend to the projection locator
        pm.aimConstraint( projectionLoc,
                          bend[1],
                          mo=False,
                          w=1,
                          aimVector=(1,0,0),
                          upVector=(0,1,0),
                          worldUpType='objectrotation',
                          worldUpObject=xform )
        
        # parent xform to the object
        pm.parent( xform, node )
        oyAux.goHome( xform )
        
        # rotate the xform to the direction
        #localDirection = Utility.getLocalVector( node, direction )
        
        rotation = pm.modeling.angleBetween( euler=True, v1=[0,1,0], v2=direction )
        xform.setAttr('r', rotation)
        
        threePointArc = Utility.createThreePointArc( xform, secondPointLoc, controller )
        circumCurvatureNode = Utility.circumCurvature( xform, secondPointLoc, controller )
        
        # to the bend
        circumCurvatureNode.attr('outputX') >> bend[0].attr('curvature')
        
        # create a curveInfo node
        curveInfo = pm.createNode('curveInfo')
        threePointArc.attr('outputCurve') >> curveInfo.attr('inputCurve')
        
        curveInfo.attr('arcLength') >> bend[0].attr('highBound')
        
        # set the bounds
        bend[0].setAttr( 'lowBound', 0 )
        
        return controller, xform
    
    
    
    #----------------------------------------------------------------------
    @staticmethod
    def smartTwist(objectToDeform, rootSurface, tipSurface, objectLength=10.0):
        """creates a smart twist, which twist the object to conform the root
        and tip of the object to the underlying surface
        """
        twist = pm.nonLinear(objectToDeform, type='twist', defaultScale=True)
        
        # parent twist to the parent of the object
        assert(isinstance(objectToDeform, pm.nodetypes.Transform))
        parent = objectToDeform.getParent()
        
        pm.parent(twist[1], parent)
        twist[0].setAttr('lowBound', 0)
        twist[0].setAttr('highBound', objectLength)
        twist[0].setAttr('startAngle', 0)
        twist[1].setAttr('t', (0,0,0))
        twist[1].setAttr('r', (90,0,0))
        twist[1].setAttr('s', (1,1,1))
        
        # create a follicle at the tip
        temp_locator = pm.spaceLocator()
        pm.parent( temp_locator, objectToDeform )
        temp_locator.setAttr('t',(0,0,objectLength))
        tip_position = pm.xform( temp_locator, q=1, ws=1, t=1 )
        pm.delete(temp_locator)
        
        p_tip, u_tip, v_tip = Utility.closestPointOnSurfaceToPoint(
            tip_position,
            tipSurface
        )
        
        #print u_tip, v_tip
        #tipFollicle = Utility.createFollicle( tipSurface, u_tip, v_tip )
        # select the surface point
        pm.select( tipSurface.name() +
                   '.uv[' + str(u_tip) + '][' +
                   str(v_tip) + ']' )
        
        tipRivet = oyAux.rivet()
        
        # create a locator under the follicle
        tipLocator = pm.spaceLocator()
        tipLocator.rename('tipLocator#')
        #pm.parent(tipLocator, tipFollicle)
        pm.parent(tipLocator, tipRivet)
        oyAux.goHome(tipLocator)
        pm.parent(tipLocator, parent)
        tipLocator.setAttr('r',(0,0,0))
        
        tipLocator_aim = pm.spaceLocator()
        tipLocator_aim.rename('tipLocator_aim#')
        #pm.parent(tipLocator_aim, tipFollicle)
        pm.parent(tipLocator_aim, tipRivet)
        oyAux.goHome(tipLocator_aim)
        tipLocator_aim.setAttr('ty', objectLength)
        
        projTipLocator_aim = Utility.createProjection(
            tipLocator_aim,
            (0,0,1),
            parent,
            (0,0,10)
        )
        
        tipLocator.setAttr('rotateOrder',1)
        
        # create an aim constraint between tipLocator_aim and tipLocator
        pm.aimConstraint( projTipLocator_aim,
                          tipLocator,
                          worldUpType='object',
                          worldUpObject=parent,
                          aimVector=(0,1,0),
                          upVector=(1,0,0)
                          )
        
        # connect tipLocators rotateZ to twists endAngle
        multDiv = pm.createNode('multiplyDivide')
        multDiv.setAttr('i2x',-1)
        
        tipLocator.attr('rz') >> multDiv.attr('i1x')
        multDiv.attr('ox') >> twist[0].attr('endAngle')
        
        return twist

    
    @staticmethod
    def setDrivenKeyframe(driven, driver, dv=0.0, v=0.0):
        """creates set driven keyframes

        :param driven: Driven attribute
        :param driver: Driver attribute
        :param dv: Driven Value
        :param v: Value
        """
        #pm.setDrivenKeyframe( self._wingParentConstraints[i], at=attr, cd=blenderAttrAsStr, dv=0.0, v=1.0 )

        # create an animcurveUU
        # connect the controller attribute to animCurves input
        # connect the output to the driven attribute
        # set the keyframes
        animCurve = None
        attr_type = driven.get(type=True)

        animCurveClass = None
        if attr_type == "doubleAngle":
            animCurveClass = pm.nt.AnimCurveUA
        else:
            # double
            # float
            # int
            animCurveClass = pm.nt.AnimCurveUU
        
        if driven.inputs():
            animCurve = driven.inputs()[0]
        
        if not animCurve or not isinstance(animCurve, animCurveClass):
            animCurve = animCurveClass()
            driver >> animCurve.input
            animCurve.output >> driven
        
        pm.setKeyframe(animCurve, f=dv, v=v)






        
